package com.yanqu.road.server.gameplayer.module.patrons;

import com.alibaba.fastjson.JSONObject;
import com.yanqu.road.dao.impl.player.UserPatronsContractDaoImpl;
import com.yanqu.road.entity.activity.args.PatronLevelArgs;
import com.yanqu.road.entity.config.patrons.*;
import com.yanqu.road.entity.config.promotion.PromotionInfo;
import com.yanqu.road.entity.config.skill.SkillInfo;
import com.yanqu.road.entity.config.skill.SkillUpgradeInfo;
import com.yanqu.road.entity.config.skin.SkinInfo;
import com.yanqu.road.entity.config.skin.SkinPatronsSkill;
import com.yanqu.road.entity.cricket.CricketData;
import com.yanqu.road.entity.cricket.config.CricketConfig;
import com.yanqu.road.entity.enums.*;
import com.yanqu.road.entity.gameconfigparam.PatronsSkillParam;
import com.yanqu.road.entity.gameconfigparam.PatronsSkillSameParam;
import com.yanqu.road.entity.log.*;
import com.yanqu.road.entity.manor.FishData;
import com.yanqu.road.entity.player.*;
import com.yanqu.road.entity.servercenter.ServerInfo;
import com.yanqu.road.entity.task.args.PatronsArgs;
import com.yanqu.road.entity.task.args.UnionRiseRankArgs;
import com.yanqu.road.entity.task.args.union.UnionActivityTaskArgs;
import com.yanqu.road.entity.union.cross.CrossUnionMember;
import com.yanqu.road.entity.wingroom.chakra.enums.WingRoomPositionTypeEnum;
import com.yanqu.road.logic.bussiness.player.UserPatronsBussiness;
import com.yanqu.road.logic.config.GameConfig;
import com.yanqu.road.logic.pb.PatronsPb;
import com.yanqu.road.logic.pb.PlayerBasePb;
import com.yanqu.road.pb.patrons.PatronsProto;
import com.yanqu.road.server.GameServer;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.activity.curio.CurioModule;
import com.yanqu.road.server.gameplayer.module.activity.jieshi.JieShiModule;
import com.yanqu.road.server.gameplayer.module.activity.patronsfund.PatronsFundModule;
import com.yanqu.road.server.gameplayer.module.activity.peakstronghold.PeakStrongholdModule;
import com.yanqu.road.server.gameplayer.module.activity.rankunionwar.RankUnionWarModule;
import com.yanqu.road.server.gameplayer.module.activity.strongholdwar.StrongholdModule;
import com.yanqu.road.server.gameplayer.module.activity.travelbook.TravelBookModule;
import com.yanqu.road.server.gameplayer.module.activity.twins.TwinsModule;
import com.yanqu.road.server.gameplayer.module.assistant.AssistantSkinModule;
import com.yanqu.road.server.gameplayer.module.banquet.BanquetModule;
import com.yanqu.road.server.gameplayer.module.base.GeneralModule;
import com.yanqu.road.server.gameplayer.module.cricket.CricketMgr;
import com.yanqu.road.server.gameplayer.module.cricket.CricketModule;
import com.yanqu.road.server.gameplayer.module.drugstore.DrugStoreModule;
import com.yanqu.road.server.gameplayer.module.grandson.GrandChildModule;
import com.yanqu.road.server.gameplayer.module.hunt.HuntModule;
import com.yanqu.road.server.gameplayer.module.inn.InnModule;
import com.yanqu.road.server.gameplayer.module.kuang.KuangModule;
import com.yanqu.road.server.gameplayer.module.manor.ManorModule;
import com.yanqu.road.server.gameplayer.module.match.MatchModule;
import com.yanqu.road.server.gameplayer.module.musicroom.MusicRoomModule;
import com.yanqu.road.server.gameplayer.module.player.*;
import com.yanqu.road.server.gameplayer.module.resource.GoodsResourceModule;
import com.yanqu.road.server.gameplayer.module.sacrifice.SacrificeModule;
import com.yanqu.road.server.gameplayer.module.skin.SkinModule;
import com.yanqu.road.server.gameplayer.module.vehicle.VehicleBoneModule;
import com.yanqu.road.server.gameplayer.module.vehicle.VehicleModule;
import com.yanqu.road.server.gameplayer.module.vehicle.VehicleSoulModule;
import com.yanqu.road.server.gameplayer.module.winery.WineryModule;
import com.yanqu.road.server.gameplayer.module.wingroom.WingRoomModule;
import com.yanqu.road.server.gameplayer.module.xiaomian.XiaoMianModule;
import com.yanqu.road.server.gameplayer.module.zhaoshang.ZsModule;
import com.yanqu.road.server.manger.ConfigMgr;
import com.yanqu.road.server.manger.DataAnalyticsMgr;
import com.yanqu.road.server.manger.ServerListMgr;
import com.yanqu.road.server.manger.activity.curio.CurioMgr;
import com.yanqu.road.server.manger.activity.douluodalu.DouLuoDaLuMgr;
import com.yanqu.road.server.manger.activity.jiuzhou.JiuZhouMgr;
import com.yanqu.road.server.manger.activity.peakseacraft.PeakSeacraftMgr;
import com.yanqu.road.server.manger.activity.rankunionwar.RankUnionWarMgr;
import com.yanqu.road.server.manger.activity.seacraft.SeacraftSouthSeaMgr;
import com.yanqu.road.server.manger.activity.suitang.SuiTangMgr;
import com.yanqu.road.server.manger.activity.tonbing.TonBingMgr;
import com.yanqu.road.server.manger.activity.tonbingchannel.ChannelTonBingMgr;
import com.yanqu.road.server.manger.activity.tongbing.ChuHanMgr;
import com.yanqu.road.server.manger.activity.tongbing.TongBingMgr;
import com.yanqu.road.server.manger.activity.unionwar.UnionPhaseWarMgr;
import com.yanqu.road.server.manger.config.*;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.manger.manor.FishPondMgr;
import com.yanqu.road.server.manger.monopolymarket.MonopolyMarketMgr;
import com.yanqu.road.server.manger.palace.PalaceActivity;
import com.yanqu.road.server.manger.palace.PalaceActivityMgr;
import com.yanqu.road.server.manger.patrons.PatronsSimpleCacheMgr;
import com.yanqu.road.server.manger.player.WorldMessageMgr;
import com.yanqu.road.server.manger.rank.RankMgr;
import com.yanqu.road.server.manger.system.SystemSwitchMgr;
import com.yanqu.road.server.manger.systemgroup.newmonopolymarket.NewMonopolyMarketMgr;
import com.yanqu.road.server.manger.union.CrossUnionMgr;
import com.yanqu.road.server.manger.wingroom.WingRoomChakraMgr;
import com.yanqu.road.server.manger.wingroom.WingRoomMgr;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.server.timer.union.cross.CrossUnionMemberAbilitySyncTask;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

public class PatronsModule extends GeneralModule {

    private Map<Integer, UserPatrons> userPatronsMap;

    private Map<Integer, Map<Integer, Integer>> talentSkillAdditionMap;

    private RandomHelper threadSafeRandom = new RandomHelper();

    /**
     * 待同步的门客列表
     */
    private Set<UserPatrons> needSyncPatrons = new HashSet<>();

    //最高等级和突破的门客
    private UserPatrons maxLevelUserPatrons;

    private final AtomicBoolean updateHistoryMaxEarnSpeed = new AtomicBoolean(false); // 是否更新历史最高赚速

    public PatronsModule(GamePlayer player) {
        super(player);
    }

    @Override
    public boolean saveData() {
        commitPatronsChange();
        List<UserPatrons> tempList = new ArrayList<>(userPatronsMap.values());
        for (UserPatrons patrons : tempList) {
            if (patrons.isInsertOption()) {
                UserPatronsBussiness.addUserPatrons(patrons);
            } else if (patrons.isUpdateOption()) {
                UserPatronsBussiness.updateUserPatrons(patrons);
            }
            if (null != patrons.getOriginalSkillList()) {
                List<UserPatronsSkill> tempSkillList = new ArrayList<>(patrons.getOriginalSkillList());
                for (UserPatronsSkill patronsSkill : tempSkillList) {
                    if (patronsSkill.isInsertOption()) {
                        UserPatronsBussiness.addUserPatronsSkill(patronsSkill);
                    } else if (patronsSkill.isUpdateOption()) {
                        UserPatronsBussiness.updateUserPatronsSkill(patronsSkill);
                    }
                }
            }
            for (UserPatronsContract contractData : patrons.getPatronsContractMap().values()) {
                if (contractData.isInsertOption()) {
                    new UserPatronsContractDaoImpl().add(contractData);
                } else if (contractData.isUpdateOption()) {
                    new UserPatronsContractDaoImpl().update(contractData);
                }
            }
        }
        return true;
    }

    @Override
    public boolean loadData() {
        userPatronsMap = UserPatronsBussiness.getUserPatronsAndSkillMap(player.getUserId());
        player.getUserInfo().setPatronsCount(userPatronsMap.size());
        talentSkillAdditionMap = SkillMgr.initTalentSkillAdditionMap(userPatronsMap);
        return true;
    }

    @Override
    public boolean afterLoadData() {
        //修复技能错误信息
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            initSkill(userPatrons, patronsInfo);
            checkPatronsCombUnlock(userPatrons, patronsInfo, false);
        }

        return true;
    }

    //退魔技能解锁
    private void checkExorcismSkillUnLock(UserPatrons userPatrons, int skillId) {
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
        for (int needUnLockSkillId : patronsInfo.getSkillUnLockMap().keySet()) {
            Map<Integer, Integer> unlockMap = patronsInfo.getSkillUnLockMap().get(needUnLockSkillId);
            if (!unlockMap.containsKey(skillId)) {
                continue;
            }
            if (userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), needUnLockSkillId) != null) {
                continue;
            }
            boolean unlock = true;
            for (int needSkillId : unlockMap.keySet()) {
                UserPatronsSkill userPatronsSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), needSkillId);
                if (userPatronsSkill.getSkillLv() < unlockMap.get(needSkillId)) {
                    unlock = false;
                    break;
                }
            }
            if (unlock) {
                SkillInfo skillInfo = SkillMgr.getSkillInfo(needUnLockSkillId);
                if (skillInfo != null) {
                    UserPatronsSkill skill = PatronsMgr.initUserPatronsSkill(userPatrons, needUnLockSkillId, ePatronsSkillType.Normal.getValue(), 1, 0);
                    addPatronsSkill(userPatrons, skill);
                    syncPatronsData(userPatrons);
                }
            }
        }
    }

    private Set<UserPatrons> checkSkillUnlock(UserPatrons userPatrons) {
        Set<UserPatrons> list = new HashSet<>();
        boolean needSync = false;
        // 孙子授学技能
        UserPatronsSkill teacherSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), GameConfig.GRAND_CHILD_TEACH_SKILL_ID);
        if (teacherSkill != null) {
            String[] arr = GameConfig.GRAND_CHILD_SKILL_UNLOCK.split("\\|");
            for (String s : arr) {
                String[] split = s.split(";");
                int skillId = Integer.parseInt(split[0]);
                int needLv = Integer.parseInt(split[1]);
                UserPatronsSkill skill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), skillId);
                if (skill != null) {
                    continue;
                }
                if (teacherSkill.getSkillLv() < needLv) {
                    continue;
                }
                skill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Normal.getValue(), 1, 0);
                addPatronsSkill(userPatrons, skill);
                needSync = true;
            }
        }
        // 其他技能
        if (needSync) {
            list.add(userPatrons);
        }
        return list;
    }

    @Override
    public void afterLogin() {
        checkPatronsResource();
        refreshCopyMaxPatronsLevel(true);
    }

    private void checkPatronsResource() {
        for (UserPatrons patrons : userPatronsMap.values()) {
            addPatronsLinkType(patrons.getPatronsId());
        }
    }

    private void addPatronsLinkType(int patronsId) {
        player.getModule(GoodsResourceModule.class).addResource(patronsId, PatronsMgr.getLinkType(patronsId));
    }

    @Override
    public void loginSendMsg() {
        if(null != userPatronsMap) {

            for (UserPatrons userPatrons : userPatronsMap.values()) {
                calcTalentSkillAddition(userPatrons);
            }
            for (UserPatrons userPatrons : new ArrayList<>(userPatronsMap.values())) {
                initTeacherSkill(userPatrons);
                checkSkillUnlock(userPatrons);
                checkKeepsakeOpen(userPatrons);
                List<UserPatronsSkill> originalSkillList = new ArrayList<>(userPatrons.getOriginalSkillList());
                for (UserPatronsSkill patronsSkill : originalSkillList) {
                    if (patronsSkill.getIsDelete() == 1) {
                        continue;
                    }
                    checkPatronsSakeAssociatedSkillUnlock(null, userPatrons, patronsSkill);
                }
            }
            //登录时候，重新设置一下门客实力值
            onPatronsChange(null);
            syncPatronsData(new ArrayList<>(userPatronsMap.values()), true);
        }
    }

    /**
     * 同步门客信息
     */
    public void syncPatronsData(List<UserPatrons> patronsList, boolean isLogin){
        for (UserPatrons userPatrons : patronsList) {
            //有百业经验 激活百业技能
            if (userPatrons.getBaiYeExp() > 0L) {
                initBaiYeSkill(userPatrons);
            }
            if (SystemOpenMgr.systemOpen(player, eSystemId.HouseBank.getValue())) {
                initSystemSkill(userPatrons, GameConfig.PATRONS_CHOUSUAN_EXP_SKILL, ePatronsSkillType.Normal.getValue());
            }

        }
        if (isLogin) {
            player.sendPacket(Protocol.U_PATRONS_INFO, PatronsPb.parsePatronsListMsg(patronsList));
        } else {
            player.sendPacket(Protocol.U_PATRONS_SYNC, PatronsPb.parsePatronsListMsg(patronsList));
        }
    }

    public void syncPatronsDataAll(boolean isLogin) {
        syncPatronsData(new ArrayList<>(userPatronsMap.values()), isLogin);
    }

    /**
     * 同步单个门客信息
     */
    public void syncPatronsData(UserPatrons userPatrons) {
        if (null != userPatrons) {
            List<UserPatrons> syncList = new ArrayList<>();
            syncList.add(userPatrons);
            syncPatronsData(syncList, false);
        }
    }

    private void initBaiYeSkill(UserPatrons userPatrons) {
        //杂艺技能初始化
        int skillId = GameConfig.PATRONS_VARIETY_SKILL_ID;
        UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), skillId);
        if (null == hasSkill) {
            SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);
            if (skillInfo != null) {
                UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Normal.getValue(), 1, 0);
                userPatrons.getOriginalSkillList().add(patronsSkill);
                onPatronsChange(userPatrons);
            }
        }
    }


    //外部随着系统解锁门客拥有的技能初始化,钱庄筹算值技能
    private void initSystemSkill(UserPatrons userPatrons, int skillId, int skillType) {
        UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(skillType, skillId);
        if (null == hasSkill) {
            SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);
            if (skillInfo != null) {
                UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, skillType, 1, 0);
                userPatrons.getOriginalSkillList().add(patronsSkill);
                onPatronsChange(userPatrons);
            }
        }
    }

    public UserPatrons getUserPatrons(int patronsId) {
        return userPatronsMap.get(patronsId);
    }

    public Map<Integer, UserPatrons> getUserPatronsMap() {
        return userPatronsMap;
    }

    public List<UserPatrons> getUserPatronsList() {
        return new ArrayList<>(userPatronsMap.values());
    }

    public List<UserPatrons> getUserPatronsList(List<Integer> patronsIdList) {
        List<UserPatrons> dataList = new ArrayList<>();
        for (int patronsId : patronsIdList) {
            UserPatrons userPatrons = getUserPatrons(patronsId);
            if (null != userPatrons) {
                dataList.add(userPatrons);
            }
        }
        return dataList;
    }

    /**
     * 判断是否门客互斥门客
     * @param patronsId
     * @return 互斥的门客
     */
    public UserPatrons checkPatronsConflict(int patronsId) {
        //获取互斥组
        List<Set<Integer>> conflictSetList = PatronsMgr.getPatronsConflictSetList();
        for (Set<Integer> set : conflictSetList) {
            if (set.contains(patronsId)) {
                //获取门客是否存在
                for (int targetPatronsId : set) {
                    UserPatrons userPatrons = userPatronsMap.get(targetPatronsId);
                    if (userPatrons != null) {
                        return userPatrons;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 增加门客
     */
    public UserPatrons addPatrons(int patronsId, eLogMoneyType master, eLogMoneyType son) {
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsId);
        UserPatrons userPatrons = null;
        if (null != patronsInfo) {
            if (!userPatronsMap.containsKey(patronsId)) {
                //门客互斥检测（不同门客ID属于同一个门客的处理）
                UserPatrons conflictPatrons = this.checkPatronsConflict(patronsId);
                if (conflictPatrons != null) {
                    return conflictPatrons;
                }
                //创建新门客
                userPatrons = new UserPatrons();
                userPatrons.setUserId(player.getUserId());
                userPatrons.setPatronsId(patronsId);
                userPatrons.setLevel(1);
                userPatrons.setStageLv(0);
                userPatrons.setBreakLv(0);
                userPatrons.setSkinId(0);
                initSkill(userPatrons, patronsInfo);
                initTeacherSkill(userPatrons);

                userPatrons.setInsertOption();
                userPatronsMap.put(userPatrons.getPatronsId(), userPatrons);
                checkPatronsCombUnlock(userPatrons, patronsInfo, true);
                calcTalentSkillAddition(userPatrons);
                onPatronsChange(userPatrons);
                syncPatronsData(userPatrons);
                player.getUserInfo().setPatronsCount(userPatronsMap.size());
                //成就、任务
                player.notifyListener(eGamePlayerEventType.HasLevelPatronsCount.getValue(), new PatronsArgs(userPatronsMap));
                player.notifyListener(eGamePlayerEventType.PatronsTotalLevel.getValue(), new PatronsArgs(userPatronsMap));
                player.notifyListener(eGamePlayerEventType.PatronsTotalCount.getValue(), new PatronsArgs(userPatronsMap));
                //触发现金礼包
                if (patronsInfo.getQuality() == eQualityType.Orange.getValue()) {
                    player.notifyListener(eGamePlayerEventType.RechargeOrangePatronsCount.getValue(), 0);
                }
                notifyAddPatrons(userPatrons);
                addPatronsLinkType(patronsId);
                // 统兵演武
                TongBingMgr.syncTongBingPatronsAbility(player, true);
                TonBingMgr.syncTonBingPatronsAbility(player, true);
                ChannelTonBingMgr.syncTonBingPatronsAbility(player, true);
                // 逐鹿隋唐
                SuiTangMgr.syncSuiTangPatronsAbility(player, true);
                ChuHanMgr.syncPatronsAbility(player, true);
            }
        }
        // 门客获得日志
        AutoLogMgr.add(new LogGetPatrons(player.getUserId(), patronsId, master.getValue(), son.getValue()));

        //结识门客埋点
        DataAnalyticsMgr.trackAddPatrons(player, patronsInfo, userPatrons);
        onPatronsListChange(new ArrayList<>(userPatronsMap.values()));
        return userPatrons;
    }

    /**
     * 通知其他系统增加了门客，初始化各个系统门客信息
     */
    private void notifyAddPatrons(UserPatrons userPatrons) {
        if (SystemOpenMgr.systemOpen(player, eSystemId.MineWar.getValue())) {
            player.getModule(MineWarModule.class).notifyAddPatrons(userPatrons);
        }
        if (SystemOpenMgr.systemOpen(player, eSystemId.TradeWar.getValue())) {
            player.getModule(TradeWarModule.class).notifyAddPatrons(userPatrons);
        }
        if (SystemOpenMgr.systemOpen(player, eSystemId.Hunt.getValue())) {
            player.getModule(HuntModule.class).notifyAddPatrons(userPatrons);
        }
        if (SystemOpenMgr.systemOpen(player, eSystemId.Sacrifice.getValue())) {
            player.getModule(SacrificeModule.class).notifyAddPatrons(userPatrons);
        }
        if (SystemOpenMgr.systemOpen(player, eSystemId.OreWar.getValue())) {
            player.getModule(OreWarModule.class).notifyAddPatrons(userPatrons);
        }

        if (SystemOpenMgr.systemOpen(player, eSystemId.PeakStrongholdActivity.getValue())) {
            player.getModule(PeakStrongholdModule.class).notifyAddPatrons(userPatrons);
        }
        //门客等级
        if (SystemOpenMgr.systemOpen(player, eSystemId.PatronLevelFund.getValue())) {
            player.notifyListener(eGamePlayerEventType.PatronsLevelFund.getValue(), new PatronLevelArgs(userPatrons.getPatronsId(), userPatrons.getLevel()));
        }

        refreshCopyMaxPatronsLevel(false);
    }

    private void checkPatronsCombUnlock(UserPatrons userPatrons, PatronsInfo patronsInfo, boolean isSendMsg) {
        List<UserPatrons> syncList = new ArrayList<>();
        CombInfo combInfo = PatronsMgr.getPatronsComb(patronsInfo.getCombId());
        if (null == combInfo) {
            return;
        }
        int combPatronsCount = 0;
        for (int patronsId : combInfo.getObjectIdList()) {
            if (null != getUserPatrons(patronsId)) {
                combPatronsCount++;
            }
        }
        if (combInfo.getCombSkillId() > 0) {//有些组合门客就一个技能，不需要加
            UserPatronsSkill combSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Comb.getValue(), combInfo.getCombSkillId());
            if (null == combSkill) {
                combSkill = PatronsMgr.initUserPatronsSkill(userPatrons, combInfo.getCombSkillId(), ePatronsSkillType.Comb.getValue(), combPatronsCount, 0);
                userPatrons.getOriginalSkillList().add(combSkill);
                syncList.add(userPatrons);
            } else {
                if (combSkill.getSkillLv() != combPatronsCount) {
                    combSkill.setSkillLv(combPatronsCount);
                    syncList.add(userPatrons);
                }
            }
        }
        for (int patronsId : combInfo.getObjectIdList()) {
            UserPatrons patrons = getUserPatrons(patronsId);
            if (null != patrons) {
                UserPatronsSkill hasSkill = patrons.getPatronsSkill(ePatronsSkillType.Comb.getValue(), combInfo.getCombSkillId());
                if (null != hasSkill && hasSkill.getSkillLv() != combPatronsCount) {
                    hasSkill.setSkillLv(combPatronsCount);
                    if (!syncList.contains(patrons)) {
                        syncList.add(patrons);
                    }
                }
            }
        }
        if (PatronsMgr.getWuShuangPatronsCombMap().containsKey(combInfo.getId()) || PatronsMgr.isUnlockPatronsComb(userPatrons.getPatronsId(), userPatronsMap)) {//解锁组合
            for (int patronsId : combInfo.getObjectIdList()) {
                UserPatrons patrons = getUserPatrons(patronsId);
                if (null != patrons) {
                    if (combInfo.getSkillId(patronsId) != 0) {
                        UserPatronsSkill hasSkill = patrons.getPatronsSkill(ePatronsSkillType.Comb.getValue(), combInfo.getSkillId(patronsId));
                        if (null == hasSkill) {
                            UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(patrons, combInfo.getSkillId(patronsId), ePatronsSkillType.Comb.getValue(), 1, 0);
                            patrons.getOriginalSkillList().add(patronsSkill);
                            if (!syncList.contains(patrons)) {
                                syncList.add(patrons);
                            }
                        }
                    }

                    //解锁新羁绊技能
                    List<Integer> skillId2 = combInfo.getSkillId2(patronsId);
                    if (skillId2 != null) {
                        for (Integer skillId : skillId2) {
                            UserPatronsSkill hasSkill2 = patrons.getPatronsSkill(ePatronsSkillType.Comb.getValue(), skillId);
                            if (null == hasSkill2) {
                                UserPatronsSkill patronsSkill2 = PatronsMgr.initUserPatronsSkill(patrons, skillId, ePatronsSkillType.Comb.getValue(), 1, 0);
                                patrons.getOriginalSkillList().add(patronsSkill2);
                                if (!syncList.contains(patrons)) {
                                    syncList.add(patrons);
                                }
                            }
                        }
                    }

                    Map<Integer, CombSkillUnlockInfo> advancedSkills = combInfo.getAdvancedSkillId(patronsId);
                    if (null != advancedSkills) {
                        for (CombSkillUnlockInfo combSkillUnlockInfo : advancedSkills.values()) {
                            Integer advancedSkillId = combSkillUnlockInfo.getSkillId();
                            if (advancedSkillId == 0) {
                                continue;
                            }
                            UserPatronsSkill patronsSkill;
                            if (combSkillUnlockInfo.getLimitSkillId() > 0) {
                                patronsSkill = patrons.getPatronsSkill(ePatronsSkillType.Comb.getValue(), combSkillUnlockInfo.getLimitSkillId());
                            } else {
                                patronsSkill = patrons.getPatronsSkill(ePatronsSkillType.Comb.getValue(), combInfo.getSkillId(patronsId));
                            }
                            if (patronsSkill != null) {
                                SkillInfo skillInfo = SkillMgr.getSkillInfo(patronsSkill.getSkillId());
                                if (null != skillInfo) {
                                    int advancedSkillLimitSkillLv;
                                    if (combSkillUnlockInfo.getLimitSkillId() > 0) {
                                        advancedSkillLimitSkillLv = combSkillUnlockInfo.getLimitSkillLevel();
                                    } else {
                                        advancedSkillLimitSkillLv = skillInfo.getMaxLevel();
                                    }
                                    if (patronsSkill.getSkillLv() >= advancedSkillLimitSkillLv) {
                                        UserPatronsSkill hasAdvancedSkill = patrons.getPatronsSkill(ePatronsSkillType.Comb.getValue(), advancedSkillId);
                                        if (hasAdvancedSkill == null) {
                                            UserPatronsSkill patronsAdvancedSkill = PatronsMgr.initUserPatronsSkill(patrons, advancedSkillId, ePatronsSkillType.Comb.getValue(), 1, 0);
                                            patrons.getOriginalSkillList().add(patronsAdvancedSkill);
                                            //受技能影响的门客加进去
                                            for (UserPatrons value : userPatronsMap.values()) {
                                                PatronsInfo info = PatronsMgr.getPatronsInfo(value.getPatronsId());
                                                if (info != null) {
                                                    if (skillInfo.getParamList().get(2) == 0 || skillInfo.getParamList().get(2) == info.getOccupation()) {
                                                        if (!syncList.contains(value)) {
                                                            syncList.add(value);
                                                        }
                                                    }
                                                }
                                            }
                                            if (!syncList.contains(patrons)) {
                                                syncList.add(patrons);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } else {
            for (int patronsId : combInfo.getObjectIdList()) {
                UserPatrons patrons = getUserPatrons(patronsId);
                if (null != patrons) {
                    if (!syncList.contains(patrons)) {
                        syncList.add(patrons);
                    }
                }
            }
        }
        if (syncList.size() > 0) {
            onPatronsListChange(syncList);
            if (isSendMsg) {
                syncPatronsData(syncList, false);
            }
        }
    }

    /**
     * 获取门客技能初始默认等级
     * @param skillId
     * @return
     */
    private int getDefaultSkillLevel(int skillId) {
        SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);
        if (skillInfo == null) {
            return 0;
        }
        //判断是否是新的技能升级配置结构
        int upgradeType = skillInfo.getUpgradeType(0);
        if (upgradeType >= 224) {//和数值规定好，技能升级224及以上，使用配置初始默认等级(224以下的技能也可以这样取，为了不影响之前的技能，先这样判断)
            //新的技能配置，取skillUpgrade第一条配置等级为默认等级
            List<SkillUpgradeInfo> skillUpgradeInfoList = SkillMgr.getSkillUpgradeInfoList(upgradeType);
            if (skillUpgradeInfoList.isEmpty()) {
                return 0;
            }
            return skillUpgradeInfoList.get(0).getLevel();
        }
        return 1;
    }

    /**
     * 初始化技能，天赋
     */
    public void initSkill(UserPatrons userPatrons, PatronsInfo patronsInfo) {
        if (null == userPatrons || null == patronsInfo) {
            return;
        }
        List<Integer> skillList;
        List<Integer> talentList;
        //晋升id 为0从门客信息取 不为0则从晋升表取技能
        if (userPatrons.getPromotionId() == 0) {
            skillList = patronsInfo.getSkillList();
            talentList = patronsInfo.getTalentList();
        } else {
            skillList = PromotionMgr.getPromotionInfoById(userPatrons.getPromotionId()).getSkillList();
            talentList = PromotionMgr.getPromotionInfoById(userPatrons.getPromotionId()).getTalentList();
        }
        if (null != skillList && null != talentList) {
            //skillList.addAll(PatronsMgr.calUnlockSkill(userPatrons, patronsInfo));
            for (int skillId : skillList) {
                UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), skillId);
                if (null == hasSkill) {
                    SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);
                    //门客厨艺技能系统解锁
                    if (null != skillInfo && skillInfo.getConsumeGoodsId().get(0) == GameConfig.COOKING_ITEM_ID && !SystemOpenMgr.systemOpen(player, eSystemId.Inn.getValue())) {
                        continue;
                    }
                    //门客退魔技能初始化没有达到条件解锁，必须另外一个技能达到多少等级才会解锁另外一个
                    if (patronsInfo.getSkillUnLockMap().containsKey(skillId)) {
                        continue;
                    }
                    // 九龙城解锁
                    if (skillId == GameConfig.KOWLOON_PATRONS_SKILL_ID) {
                        // 系统屏蔽
                        if (SystemSwitchMgr.getLockSystemIdList().contains(eSystemId.Kowloon.getValue())) {
                            continue;
                        }
                        ServerInfo serverInfo = ServerListMgr.getServerInfo(GameServer.getInstance().getServerId());
                        if (serverInfo.getSpecialServer() != eSpecialServerType.Kowloon.getValue() && !SystemOpenMgr.systemOpen(player, eSystemId.Kowloon.getValue())) {
                            continue;
                        }
                    }
                    int skillLevel = 1;
                    if (SkillMgr.isPatronsCricketFateSkill(skillId)) {
                        skillLevel = 0;
                    } else {
                        //获取技能默认等级
                        skillLevel = this.getDefaultSkillLevel(skillId);
                    }
                    UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Normal.getValue(), skillLevel, 0);
                    addPatronsSkill(userPatrons, patronsSkill);
                }
            }
        }
        if (null != talentList) {
            for (int skillId : talentList) {
                UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Talent.getValue(), skillId);
                if (null == hasSkill) {
                    UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Talent.getValue(), 1, 0);
                    addPatronsSkill(userPatrons, patronsSkill);
                }
            }
        }

        //初始招商技能
        //门客招商技能系统解锁
        if (player.getModule(ZsModule.class).isZsSystemOpen()) {
            for (int skillId : ConfigMgr.getZsSkillList()) {
                UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), skillId);
                if (null == hasSkill) {
                    SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);
                    if (skillInfo == null) {
                        continue;
                    }
                    //职业不同
                    if (skillInfo.getParamList().get(3) != patronsInfo.getOccupation()) {
                        continue;
                    }
                    UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Normal.getValue(), 1, 0);
                    addPatronsSkill(userPatrons, patronsSkill);
                }
            }
        }

        // 初始化特殊门客皮肤技能
        for (Map.Entry<Integer, Integer> entry : player.getModule(SkinModule.class).getPatronsSkillMap(userPatrons.getPatronsId()).entrySet()) {
            int skillId = entry.getKey();
            int level = entry.getValue();
            UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), skillId);
            SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);
            if (null == hasSkill) {
                UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Normal.getValue(), 1, 0);
                // 皮肤解锁有初始等级
                patronsSkill.setSkillLv(skillInfo.getParamList().get(3));
                addPatronsSkill(userPatrons, patronsSkill);
                hasSkill = patronsSkill;
            }
            hasSkill.setSkinSkillLevel((level - 1) * skillInfo.getParamList().get(2));
        }

        //初始化皮肤技能
        Map<Integer, UserSkin> skinMap = player.getModule(SkinModule.class).getUserSkinMap();
        for (UserSkin userSkin : skinMap.values()) {
            SkinInfo skinInfo = SkinMgr.getSkinInfo(userSkin.getSkinId());
            if (skinInfo == null) {
                log.error("skinInfo is null. skinId : {}", userSkin.getSkinId());
                continue;
            }
            if (skinInfo.getType() == eSkinType.Patrons.getValue() && skinInfo.getBelongRole() == userPatrons.getPatronsId()) {//门客皮肤
                for (SkinPatronsSkill skinPatronsSkill : skinInfo.getSkinPatronsSkillList()) {
                    UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Skin.getValue(), skinPatronsSkill.getSkillId());
                    if (null == hasSkill) {
                        UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skinPatronsSkill.getSkillId(),
                                ePatronsSkillType.Skin.getValue(), 1, 0);
                        addPatronsSkill(userPatrons, patronsSkill);
                    }
                }
            }
        }
        //同步精进解锁技能
        addStageSkill(userPatrons);

        //初始化门客新羁绊技能
        initPatronsNewCombSkill(userPatrons);

        // 删除已经没用的技能
        for (UserPatronsSkill patronsSkill : userPatrons.getSkillList()) {
            SkillInfo skillInfo = SkillMgr.getSkillInfo(patronsSkill.getSkillId());
            if (skillInfo == null || skillInfo.getType() == eSkillType.HuaShiGangBattleDamageAddition.getValue()) {
                deletePatronsSkill(userPatrons, patronsSkill.getSkillId());
                log.error("userId {} patronsId {} skillId {} skillType {}, set skill delete.",
                        getUserId(), userPatrons.getPatronsId(), patronsSkill.getSkillId(), eSkillType.HuaShiGangBattleDamageAddition.getValue());
            }
        }

    }

    private void initPatronsNewCombSkill(UserPatrons userPatrons) {

    }

    private void initTeacherSkill(UserPatrons userPatrons) {
        if (player.getModule(GrandChildModule.class).canInitTeacherSkill(userPatrons.getPatronsId())) {
            int skillId = GameConfig.GRAND_CHILD_TEACH_SKILL_ID;
            UserPatronsSkill hasSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), skillId);
            if (hasSkill == null) {
                UserPatronsSkill patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Normal.getValue(), 1, 0);
                addPatronsSkill(userPatrons, patronsSkill);
            }
        }
    }

    private void addStageSkill(UserPatrons userPatrons) {
        int curStageLv = userPatrons.getStageLv();
        for (int lv = 0; lv <= curStageLv; lv++) {
            PatronsStage patronsStage = PatronsMgr.getPatronsStage(lv);
            if (patronsStage != null) {
                SkillInfo skillInfo = SkillMgr.getSkillInfo(patronsStage.getAddSkillId());
                if (skillInfo != null) {
                    if (!userPatrons.isContainsSkill(ePatronsSkillType.Normal.getValue(), skillInfo.getId())) {
                        player.getModule(PatronsModule.class).addPatronsSkill(userPatrons, PatronsMgr.initUserPatronsSkill(userPatrons, skillInfo.getId(), ePatronsSkillType.Normal.getValue(), 1, 0));
                    }
                }
            }
        }
    }

    /**
     * 升级
     */
    public int patronsUpgrade(int patronsId, int times, int goodsIndex) {
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (null == userPatrons) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
        if (patronsInfo == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }

        if (hasTalent(patronsInfo.getTalentList(), eSkillType.CopyMaxPatronsLevelSkill.getValue())) {
            return GameErrorCode.E_PATRONS_LEVEL_MAX;
        }

        PatronsBreak patronsBreak = PatronsMgr.getPatronsBreak(userPatrons.getBreakLv());
        int embroideryMeter = player.getModule(SkinVolumeModule.class).getSkinVolumeData().getEmbroideryMeter();
        int curMaxLv = PatronsMgr.getCurMaxLevel(patronsBreak.getBreakLv(), patronsId, embroideryMeter);
        if (userPatrons.getLevel() >= curMaxLv) {
            return GameErrorCode.E_PATRONS_LEVEL_MAX;
        }
        int upgradeCost = ConfigMgr.getPatronsUpgradeCost(patronsInfo.getQuality());
        int i;
        int addLv = 0;
        for (i = 0; i < times; i++) {
            PatronsUpgradeInfo nextUpgradeInfo = PatronsMgr.getPatronsUpgradeInfo(userPatrons.getLevel() + 1);
            if (null == nextUpgradeInfo) {
                if (0 == i) {
                    return GameErrorCode.E_PATRONS_LEVEL_MAX;
                } else {
                    break;
                }
            }
            //检查配置
            if (goodsIndex != 0 && (nextUpgradeInfo.getConsumeNew() == null || nextUpgradeInfo.getConsumeNew().isNothing())) {
                return GameErrorCode.E_REQUEST_ARGUMENT_ERROR;
            }
            PatronsUpgradeInfo upgradeInfo = PatronsMgr.getPatronsUpgradeInfo(userPatrons.getLevel());
            Property consume = PatronsMgr.getPatronsUpgradeConsume(upgradeInfo, upgradeCost, goodsIndex);
            if (!player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.Patrons, eLogMoneyType.PatronsUpgrade)) {
                if (0 == i) {
                    return GameErrorCode.E_BAG_PATRONS_EXP_NO_ENOUGH;
                } else {
                    break;
                }
            } else {//增加等级
                int oldLv = userPatrons.getLevel();
                userPatrons.setLevel(userPatrons.getLevel() + 1);
                addLv++;
                // 门客升级日志
                AutoLogMgr.add(new LogPatronsUpgrade(player.getUserId(), patronsId, oldLv, userPatrons.getLevel(), PropertyHelper.parsePropertyToString(consume)));
                if (userPatrons.getLevel() >= curMaxLv) {
                    break;
                }
            }
        }
        refreshCopyMaxPatronsLevel(false);
        //成就、任务
        player.notifyListener(eGamePlayerEventType.PatronsUpgradeTimes.getValue(), addLv);
        player.notifyListener(eGamePlayerEventType.PatronsUpgradeTimesForALi.getValue(), addLv);
        player.notifyListener(eGamePlayerEventType.UnionPatronsUpgradeTimes.getValue(), new UnionActivityTaskArgs(UnionActivityTaskArgs.TYPE_ADD_VALUE, addLv, player.getUserInfo().getUnionUid()));
        player.notifyListener(eGamePlayerEventType.HasLevelPatronsCount.getValue(), new PatronsArgs(userPatronsMap));
        player.notifyListener(eGamePlayerEventType.PatronsTotalLevel.getValue(), new PatronsArgs(userPatronsMap));
        //门客等级
        player.notifyListener(eGamePlayerEventType.PatronsLevelFund.getValue(), new PatronLevelArgs(patronsId, userPatrons.getLevel()));
        //小面任务
        player.getModule(XiaoMianModule.class).addPatronsUpgradeTimes(addLv);
        onPatronsChange(userPatrons);

        //升级埋点
        //DataAnalyticsMgr.trackPatronsUpgrade(player, patronsInfo, userPatrons, addLv);
        return 0;
    }

    /**
     * 门客突破
     */
    public int patronsBreak(int patronsId) {
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (null == userPatrons) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
        if (patronsInfo == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        if (hasTalent(patronsInfo.getTalentList(), eSkillType.CopyMaxPatronsLevelSkill.getValue())) {
            return GameErrorCode.E_PATRONS_BREAK_LEVEL_MAX;
        }
        PatronsBreak nextPatronsBreak = PatronsMgr.getPatronsBreak(userPatrons.getBreakLv() + 1);
        if (null == nextPatronsBreak) {
            return GameErrorCode.E_PATRONS_BREAK_LEVEL_MAX;
        }
        PatronsBreak patronsBreak = PatronsMgr.getPatronsBreak(userPatrons.getBreakLv());
        if (userPatrons.getLevel() < patronsBreak.getCurrentMaxLv()) {
            return GameErrorCode.E_PATRONS_LEVEL_NO_ENOUGH;
        }
        int embroideryMeter = player.getModule(SkinVolumeModule.class).getSkinVolumeData().getEmbroideryMeter();
        if (GameConfig.PATRONS_UPGRADE_MAX_LEVEL + PatronsMgr.getSkillAddLevel(patronsId, embroideryMeter) <= patronsBreak.getCurrentMaxLv()) {
            return GameErrorCode.E_PATRONS_BREAK_LEVEL_MAX;
        }
        //消耗
        if (!player.getModule(CurrencyModule.class).removeCurrency(patronsBreak.getConsume(), eLogMoneyType.Patrons, eLogMoneyType.PatronsBreak)) {
            //触发现金礼包
            player.notifyListener(eGamePlayerEventType.RechargePatronsBreak.getValue(), 0);
            player.notifyListener(eGamePlayerEventType.RechargePatronsBreakNew.getValue(), 0);
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }
        //增加精进等级
        int oldLv = userPatrons.getBreakLv();
        userPatrons.setBreakLv(userPatrons.getBreakLv() + 1);

        // 门客突破日志
        AutoLogMgr.add(new LogPatronsBreak(player.getUserId(), patronsId, oldLv, userPatrons.getBreakLv(), PropertyHelper.parsePropertyToString(patronsBreak.getConsume())));
        //成就、任务
        player.notifyListener(eGamePlayerEventType.PatronsTotalBreakTimes.getValue(), new PatronsArgs(userPatronsMap));
        onPatronsChange(userPatrons);

        //下一级升级是否足够
        PatronsBreak nextPatronsBreakNew = PatronsMgr.getPatronsBreak(userPatrons.getBreakLv() + 1);
        if (nextPatronsBreakNew != null) {
            if (!player.getModule(CurrencyModule.class).currencyIsEnough(nextPatronsBreakNew.getConsume())) {
                player.notifyListener(eGamePlayerEventType.RechargePatronsBreakNew.getValue(), 0);
            }
        }

        refreshCopyMaxPatronsLevel(false);
        return 0;
    }

    /**
     * 门客精进
     */
    public int patronsStage(int patronsId) {
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (null == userPatrons) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
        //如果晋升id不为0,查看晋升后是否可精进
        int promotionId = userPatrons.getShowPromotionId();
        if (promotionId != 0) {
            PromotionInfo promotionInfo = PromotionMgr.getPromotionInfoById(promotionId);
            if (0 == promotionInfo.getCanStage()) {
                return GameErrorCode.E_PATRONS_CAN_NO_STAGE;
            }
        }
        //晋升id为0,查看patronsInfo是否可精进
        else {
            if (!patronsInfo.isCanStage()) {
                return GameErrorCode.E_PATRONS_CAN_NO_STAGE;
            }
        }

        PatronsStage nextPatronsStage = PatronsMgr.getPatronsStage(userPatrons.getStageLv() + 1);
        if (null == nextPatronsStage) {
            return GameErrorCode.E_PATRONS_STAGE_LEVEL_MAX;
        }
        PatronsStage patronsStage = PatronsMgr.getPatronsStage(userPatrons.getStageLv());
        if (userPatrons.getLevel() < patronsStage.getNeedPatronsLv()) {
            return GameErrorCode.E_PATRONS_LEVEL_NO_ENOUGH;
        }
        try {
            String param = GameConfig.PATRONS_STAGE_UPGRADE_LIMIT;
            String[] split = param.split("\\|");
            String limit = split[userPatrons.getStageLv()];
            String[] limitSplit = limit.split(";");
            int needNum = Integer.parseInt(limitSplit[0]);
            int needStageLv = Integer.parseInt(limitSplit[1]);
            int haveNum = 0;
            for (UserPatrons patrons : getUserPatronsMap().values()) {
                if (patrons.getStageLv() >= needStageLv) {
                    haveNum++;
                }
            }
            if (haveNum < needNum) {
                return GameErrorCode.E_PATRONS_STAGE_NUM_NOENOUGH;
            }
        } catch (Exception e) {
            log.error("PATRONS_STAGE_UPGRADE_LIMIT config is error, userId:{}, patronsId:{}", getUserId(), patronsId);
            return GameErrorCode.E_PATRONS_STAGE_CONFIG_EXCEPTION;
        }
        //消耗
        if (!player.getModule(CurrencyModule.class).removeCurrency(patronsStage.getConsume(), eLogMoneyType.Patrons, eLogMoneyType.PatronsStage)) {
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }
        //增加精进等级
        int oldLv = userPatrons.getStageLv();
        userPatrons.setStageLv(patronsStage.getStageLv() + 1);
        addStageSkill(userPatrons);
        calcTalentSkillAddition(userPatrons);
        if (userPatrons.getLeaderPromotionId() > 0) {
            player.getModule(CombLeaderModule.class).syncCombLeaderData(userPatrons.getPatronsId());
        }
        onPatronsChange(null);

        //群英天赋
        if (hasTalent(patronsInfo.getTalentList(), eSkillType.CrossUnionWarAbilityAddition.getValue())) {
            //商会战
            UnionPhaseWarMgr.patronsAbilityChange(player.getUserInfo(), getUserPatronsList(), getUserPatronsMap());
        }

        // 统兵演武天赋
        if (hasTalent(patronsInfo.getTalentList(), eSkillType.TongBingTeamMaxSoldierAddi.getValue())) {
            TongBingMgr.syncTongBingPatronsAbility(player, true);
            TonBingMgr.syncTonBingPatronsAbility(player, true);
            ChannelTonBingMgr.syncTonBingPatronsAbility(player, true);
        }

        // 楚汉争霸天赋
        if (hasTalent(patronsInfo.getTalentList(), eSkillType.ChuHanTeamMaxSoldierAddi.getValue())) {
            ChuHanMgr.syncPatronsAbility(player, true);
        }

        // 逐鹿隋唐天赋
        if (hasTalent(patronsInfo.getTalentList(), eSkillType.SuiTangTeamMaxSoldierAddi.getValue())) {
            SuiTangMgr.syncSuiTangPatronsAbility(player, true);
        }

        WorldMessageMgr.worldNotify(eMessageType.PatronsStage.getValue(), player.getUserInfo().getCity(), player.getTitleId(),
                player.getUserInfo().getNickName(), patronsId, userPatrons.getStageLv());

        // 门客升阶日志
        AutoLogMgr.add(new LogPatronsStage(player.getUserId(), patronsId, oldLv, userPatrons.getStageLv(), PropertyHelper.parsePropertyToString(patronsStage.getConsume())));
        //升星埋点
        DataAnalyticsMgr.trackPatronsStage(player, patronsInfo, userPatrons, 1);
        return 0;
    }

    private boolean hasTalent(List<Integer> talentList, int skillType) {
        boolean hasTalent = false;
        for (int talentId : talentList) {
            SkillInfo skillInfo = SkillMgr.getSkillInfo(talentId);
            if (skillInfo != null) {
                if (skillInfo.getType() == skillType) {
                    hasTalent = true;
                    break;
                }
            }
        }
        return hasTalent;
    }

    public void calcTalentSkillAddition(UserPatrons userPatrons){
        boolean needNotifyHuntChange = false;
        for (UserPatronsSkill patronsSkill : userPatrons.getOriginalSkillList()) {//这里是根据精进等级修正技能等级
            if (patronsSkill.getIsDelete() == 1) {
                continue;
            }
            if (patronsSkill.getType() != ePatronsSkillType.Talent.getValue()) {
                continue;
            }
            int lv = userPatrons.getStageLv();
            SkillInfo skillInfo = SkillMgr.getSkillInfo(patronsSkill.getSkillId());
            if (skillInfo != null) {
                if (skillInfo.getMaxLevel() == -2) {
                    lv = 0;
                    List<SkillUpgradeInfo> skillUpgradeInfoList = SkillMgr.getSkillUpgradeInfoList(skillInfo.getUpgradeType(0));
                    for (SkillUpgradeInfo skillUpgradeInfo : skillUpgradeInfoList) {
                        if (skillUpgradeInfo.getConsumeCount() != 0 && skillUpgradeInfo.getLevel() > lv && userPatrons.getStageLv() >= skillUpgradeInfo.getConsumeCount()) {
                            lv = skillUpgradeInfo.getLevel();
                        }
                    }
                    patronsSkill.setSkillLv(lv + 1);
                }
            }
            patronsSkill.setSkillLv(lv + 1);
        }

        //根据精进修正凤魁的天赋技能等级
        UserCombLeaderData combLeaderData = player.getModule(CombLeaderModule.class).getUserCombLeaderDataByPatronsId(userPatrons.getPatronsId());
        if (combLeaderData != null) {
            PromotionInfo promotionInfo = PromotionMgr.getPromotionInfoById(combLeaderData.getPromoteId());
            if (promotionInfo != null) {
                int stageLv = userPatrons.getStageLv();
                for (Integer skillId : promotionInfo.getTalentList()) {
                    if (skillId == 0) {
                        continue;
                    }
                    int newSkillLv = stageLv + 1;
                    combLeaderData.setSkillLv(skillId, newSkillLv);
                }
                //更新门客身上的凤魁技能
                userPatrons.setCombLeaderSkillList(new ArrayList<>(combLeaderData.getSkillMap().values()));
            }
        }

        talentSkillAdditionMap = SkillMgr.initTalentSkillAdditionMap(userPatronsMap);
        if (needNotifyHuntChange) {
            player.getModule(HuntModule.class).notifyPatronsStage(userPatrons);
        }
    }

    /**
     * 技能升级
     */
    public int skillUpgrade(int patronsId, int skillId, int type, int goodsIndex, int times) {
        if (times < 1 || times > 10) {    //v4.5判断一次还是十连
            return GameErrorCode.E_PATRONS_SKILL_UPGRADE_TIME_ERROR;
        }
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (null == userPatrons) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        UserPatronsSkill userPatronsSkill = userPatrons.getPatronsSkill(type, skillId);
        if (null == userPatronsSkill) {
            return GameErrorCode.E_PATRONS_SKILL_NO_FOUND;
        }
        SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);

        //通过门客技能类型判断如果是组合类型是否已经解锁
        int checkCombSkillResult = checkCombSkill(type, skillId, patronsId);
        if (checkCombSkillResult != 0) {
            return checkCombSkillResult;
        }

        int skillLv = userPatronsSkill.getSkillLv();
        int upgradeType = skillInfo.getUpgradeType(goodsIndex);

        //查看当前技能最高可以升到的等级
        int maxLevel = getMaxLevel(userPatronsSkill, skillInfo, type, patronsId);

        if (maxLevel > 0 && times != 1 && skillLv + times > maxLevel) {//十连升级下 如果有最大等级限制且不满十次满级 则取实际升级次数
            times = maxLevel - skillLv;
        }
        //v4.5 单次升级取当前技能下一级升级信息，十连取技能十连最后一级的升级需求,取不到则返回
        if (null == SkillMgr.getSkillUpgradeInfo(upgradeType, skillLv + times) || times < 1) {
            return GameErrorCode.E_PATRONS_SKILL_LEVEL_MAX;
        }

        //盗墓技能，上一个技能需要满级
        if (skillInfo.getType() == eSkillType.ZhangQiLingSkill.getValue()) {
            List<Integer> skillList = StringUtils.stringToIntegerList(GameConfig.PATRONS_ZHANGQILING_EX_SKILLS, "\\|");
            int idx = skillList.indexOf(skillId);
            if (idx > 0) {
                int preSkillId = skillList.get(idx - 1);
                SkillInfo preSkill = SkillMgr.getSkillInfo(preSkillId);
                UserPatronsSkill patronsSkill = userPatrons.getPatronsSkill(preSkillId);
                if (patronsSkill != null && preSkill != null) {
                    int preMax = getMaxLevel(patronsSkill, preSkill, type, patronsId);
                    if (preMax > patronsSkill.getSkillLv()) {
                        return GameErrorCode.E_PATRONS_SKILL_NO_UPGRADE;
                    }
                }
            }
        }

        // 通过maxLevel查看是否够可以升级
        int checkMaxLevelResult = checkSkillMaxLevel(skillId, upgradeType, maxLevel, skillLv, times, type, userPatrons);
        if (checkMaxLevelResult != 0) {
            return checkMaxLevelResult;
        }
        //秦始皇活动结束也得升级... 判断一下是否秦始皇的特殊处理
        int checkQinEmperorSkillResult = checkQinEmperorSkill(skillId, patronsId, upgradeType, skillLv, times);
        if (checkQinEmperorSkillResult != 0) {
            return checkQinEmperorSkillResult;
        }
        int checkOtherSkillAddThisSkillSkillResult = checkOtherSkillAddThisSkillSkill(skillId, patronsId, upgradeType, skillLv, times);
        if (checkOtherSkillAddThisSkillSkillResult != 0) {
            return checkOtherSkillAddThisSkillSkillResult;
        }
        //组合光环等级汇总技能
        int checkComSkillDeMaxLevelSkillResult = this.checkComSkillDeMaxLevelSkill(skillInfo, userPatrons, skillLv, goodsIndex, times);
        if (checkComSkillDeMaxLevelSkillResult != 0) {
            return checkComSkillDeMaxLevelSkillResult;
        }

        //取出消耗
        SkillUpgradeInfo skillUpgradeInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, skillLv);
        Integer consumeId = skillInfo.getConsumeGoodsId().get(goodsIndex);
        Property consume = null;
        long needConsume;
        if (skillInfo.getType() == eSkillType.FoxSkill204.getValue() || skillInfo.getType() == eSkillType.FoxSkill205.getValue()) {
            needConsume = 0;
            for (int i = 1; i < skillLv + times; i++) {//统计从1级开始升到指定等级需要的总数值
                SkillUpgradeInfo thisGradeUpdateInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, i);
                needConsume += thisGradeUpdateInfo.getConsumeCount();
            }
        } else {
            if (times == 1) {              //v4.5烹饪百业筹算授学经验与其他消耗统一计算
                needConsume = skillUpgradeInfo.getConsumeCount();
                //技能组低等级升级消耗处理
                needConsume = this.skillConsumeHandle(patronsId, skillId, skillLv, needConsume);
            } else {
                needConsume = 0;
                for (int i = 0; i < times; i++) {  //v4.5升级几次就拿后面几级需要的道具
                    SkillUpgradeInfo thisGradeUpdateInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, skillLv + i);
                    //技能组低等级升级消耗处理
                    needConsume += this.skillConsumeHandle(patronsId, skillId, skillLv + i, thisGradeUpdateInfo.getConsumeCount());
                }
            }
        }

        //根据升级类型扣除消耗
        SkillUpgradeDeductConsumeResult result = removeConsume(consumeId, needConsume, goodsIndex, skillInfo, userPatrons);
        if (result.getRet() != 0) { //不足则返回
            return result.getRet();
        }
        consume = result.getConsume();

        //升级
        int oldLv = userPatronsSkill.getSkillLv();
        if (times == 1) {
            userPatronsSkill.setSkillLv(userPatronsSkill.getSkillLv() + 1);
        } else {
            userPatronsSkill.setSkillLv(userPatronsSkill.getSkillLv() + times);
        }
        // 门客技能升级日志
        AutoLogMgr.add(new LogPatronsSkillUpgrade(player.getUserId(), patronsId, skillId, type, oldLv, userPatronsSkill.getSkillLv(), PropertyHelper.parsePropertyToString(consume)));
        onPatronsChange(userPatrons);

        if (ePatronsSkillType.Normal.getValue() == type && skillInfo.getType() == eSkillType.DispatchEarn.getValue()) {//派遣技能特殊处理
            String unionUid = player.getUserInfo().getUnionUid();
            CrossUnionMgr.userPatronDispatchSkillChangeNotify(unionUid, player.getUserId(), userPatrons);
        }

        //检查门客信物伴生技能解锁
        checkPatronsSakeAssociatedSkillUnlock(skillInfo, userPatrons, userPatronsSkill);

        //进阶技能同步受影响的门客
     /*   List<UserPatrons> syncList = new ArrayList<>();
        if (skillInfo.getType() == eSkillType.SpecialOccupationPatronsAbilityAddRatio.getValue()) {
            for (UserPatrons patrons : userPatronsMap.values()) {
                PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patrons.getPatronsId());
                if (patronsInfo != null) {
                    if (skillInfo.getParamList().get(2) == 0 || skillInfo.getParamList().get(2) == patronsInfo.getOccupation()) {
                        syncList.add(patrons);
                    }
                }
            }
            onPatronsListChange(syncList);
        }*/

        if (ePatronsSkillType.Comb.getValue() == type) {
            checkPatronsCombUnlock(userPatrons, PatronsMgr.getPatronsInfo(userPatrons.getPatronsId()), true);
        }

        Set<UserPatrons> patronsSet = checkSkillUnlock(userPatrons);
        //退魔技能，技能升级达到要求解锁其他技能
        checkExorcismSkillUnLock(userPatrons,skillId);

        patronsSet.add(userPatrons);
        syncPatronsData(new ArrayList<>(patronsSet),false);
        onPatronsListChange(new ArrayList<>(userPatronsMap.values()));


        //任务、成就
        player.notifyListener(eGamePlayerEventType.PatronsSkillUpgradeTimes.getValue(), times);
        player.notifyListener(eGamePlayerEventType.PatronsTypeSkillUpgradeTimes.getValue(), new UserPatronsSkillUpgradeTypeArgs(skillInfo.getType(), times));
        player.notifyListener(eGamePlayerEventType.PatronsSkillTotalUpgradeTimes.getValue(), new PatronsArgs(userPatronsMap));
        player.notifyListener(eGamePlayerEventType.NewPatronsFundPatronsSkill.getValue(), 0);

        //升级埋点
        //DataAnalyticsMgr.trackSkillUpgrade(player, PatronsMgr.getPatronsInfo(patronsId), userPatrons, userPatronsSkill, oldLv);
        if (skillInfo.getType() == eSkillType.BanquetJoinPopularitySkill.getValue()) {
            player.getModule(BanquetModule.class).syncAutoInfo();
        }
        return 0;
    }

    /**
     * 升级技能消耗处理
     * @param patronsId
     * @param skillId
     * @param skillLevel
     * @param needConsume
     * @return
     */
    private long skillConsumeHandle(int patronsId, int skillId, int skillLevel, long needConsume) {
        if (needConsume <= 0) {
            return needConsume;
        }
        UserPatrons userPatrons = this.getUserPatrons(patronsId);
        if (userPatrons == null) {
            return needConsume;
        }
        UserPatronsSkill patronsSkill = userPatrons.getPatronsSkill(skillId);
        if (patronsSkill == null) {
            return needConsume;
        }
        //技能组低等级升级消耗处理
        String key = patronsId + "#" + skillId;
        List<PatronsSkillSameParam> patronsSkillSameParamList = GameConfig.PATRONS_SKILL_SAME_PARAM_LIST;
        for (PatronsSkillSameParam param : patronsSkillSameParamList) {
            //判断是在门客#技能组中
            if (!param.getPatronsSkillSet().contains(key)) {
                continue;
            }
            //获取一下技能组的最大等级
            int maxLv = 0;
            for (String patronsIdSkillId : param.getPatronsSkillSet()) {
                String[] patronsIdSkillIdArr = patronsIdSkillId.split("#");
                int checkPatronsId = Integer.parseInt(patronsIdSkillIdArr[0]);
                int checkSkillId = Integer.parseInt(patronsIdSkillIdArr[1]);
                UserPatrons checkUserPatrons = this.getUserPatrons(checkPatronsId);
                if (checkUserPatrons == null) {
                    continue;
                }
                UserPatronsSkill checkPatronsSkill = checkUserPatrons.getPatronsSkill(checkSkillId);
                if (checkPatronsSkill == null) {
                    continue;
                }
                if (checkPatronsSkill.getSkillLv() > maxLv) {
                    maxLv = checkPatronsSkill.getSkillLv();
                }
            }
            //判断是否大于等于最大等级
            if (skillLevel >= maxLv) {
                continue;
            }
            BigDecimal rateBig = BigDecimal.valueOf(param.getRate()).divide(BigDecimal.valueOf(1000));
            needConsume = BigDecimal.valueOf(needConsume).multiply(rateBig).setScale(0, RoundingMode.UP).longValue();
            return needConsume;
        }
        return needConsume;
    }

    /**
     * 检查是否有信物伴生技能
     */
    private void checkPatronsSakeAssociatedSkillUnlock(SkillInfo skillInfo, UserPatrons userPatrons, UserPatronsSkill userPatronsSkill) {
        if (skillInfo == null) {
            skillInfo = SkillMgr.getSkillInfo(userPatronsSkill.getSkillId());
        }
        if (skillInfo == null || skillInfo.getType() != eSkillType.PatronsKeepSakeLinkAddtition.getValue()) {
            return;
        }
        List<Integer> skillList = ConfigMgr.getPatronsSakeUnlockSkillList(userPatronsSkill.getSkillLv());
        for (Integer skillId : skillList) {
            UserPatronsSkill patronsSkill = userPatrons.getPatronsSkill(skillId);
            if (patronsSkill == null) {
                UserPatronsSkill uk = PatronsMgr.initUserPatronsSkill(userPatrons, skillId, ePatronsSkillType.Normal.getValue(), 1, 0);
                addPatronsSkill(userPatrons, uk);
                log.info("玩家{} 门客{} 解锁信物技能 {}", player.getUserId(), userPatrons.getPatronsId(), skillId);
            }
        }
    }


    /**
     * 根据类型扣除技能升级所需道具，经验
     */
    public SkillUpgradeDeductConsumeResult removeConsume(int consumeId, long needConsume, int goodsIndex, SkillInfo skillInfo, UserPatrons userPatrons) {
        Property consume;
        if (needConsume == 0) {
            return new SkillUpgradeDeductConsumeResult(0, new Property());
        }
        if (skillInfo.getType() == eSkillType.FoxSkill204.getValue()) {
            long qualificationNeed = needConsume;
            long qualificationHave = getQualificationExcludeEq(userPatrons.getPatronsId());
            if (qualificationNeed > qualificationHave) {
                return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_PATRONS_SKILL_LV_UP_NOT_ENOUGH, new Property());
            }
            return new SkillUpgradeDeductConsumeResult(0, new Property(consumeId, needConsume));
        } else if (skillInfo.getType() == eSkillType.FoxSkill205.getValue()) {
            int needSkillId = consumeId;
            UserPatronsSkill checkPatronsSkill = userPatrons.getPatronsSkill(needSkillId);
            if (checkPatronsSkill == null || checkPatronsSkill.getSkillLv() < needConsume) {
                return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_PATRONS_SKILL_LV_UP_NOT_ENOUGH, new Property());
            }
            return new SkillUpgradeDeductConsumeResult(0, new Property(consumeId, needConsume));
        } else {
            if (consumeId == GameConfig.COOKING_ITEM_ID) {
                if (userPatrons.getCookExp() < needConsume) {
                    return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_PATRONS_COOK_EXP, new Property()); //升级经验不足
                }
                long preCookExp = userPatrons.getCookExp();
                userPatrons.setCookExp(preCookExp - needConsume);
                // 加个日志
                AutoLogMgr.add(new LogPatronsCook(
                        player.getUserId(),
                        userPatrons.getPatronsId(),
                        false,
                        preCookExp,
                        needConsume,
                        userPatrons.getCookExp()
                ));
                //刷新门客厨艺值
                syncPatronsData(userPatrons);
                consume = new Property(GameConfig.COOKING_ITEM_ID, BigInteger.valueOf(needConsume));
                //百业技能（杂艺）
            } else if (consumeId == GameConfig.PATRONS_BAIYE_EXP_ID) {
                if (userPatrons.getBaiYeExp() < needConsume) {
                    return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_PATRONS_BAIYE_EXP, new Property());//升级经验不足
                }
                userPatrons.setBaiYeExp(userPatrons.getBaiYeExp() - needConsume);

                //刷新门客
                syncPatronsData(userPatrons);
                consume = new Property(GameConfig.PATRONS_BAIYE_EXP_ID, BigInteger.valueOf(needConsume));
            } else if (consumeId == GameConfig.PATRONS_CHOUSUAN_EXP_ID) {//筹算经验
                if (userPatrons.getChouSuanExp() < needConsume) {
                    return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_PATRONS_CHOUSUAN_EXP, new Property());//升级经验不足
                }
                userPatrons.setChouSuanExp(userPatrons.getChouSuanExp() - needConsume);
                //刷新门客
                syncPatronsData(userPatrons);
                consume = new Property(GameConfig.PATRONS_CHOUSUAN_EXP_ID, BigInteger.valueOf(needConsume));
            } else if (consumeId == GameConfig.GRAND_CHILD_TEACH_ITEM_ID) {
                //消耗授学经验
                if (userPatrons.getTeacherExp() < needConsume) {
                    return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_PATRONS_TEACHER_EXP, new Property());
                }
                userPatrons.setTeacherExp(userPatrons.getTeacherExp() - needConsume);
                syncPatronsData(userPatrons);
                consume = new Property(consumeId, BigInteger.valueOf(needConsume));
            } else {
                //消耗升级道具
                List<Integer> consumeGoodsId = skillInfo.getConsumeGoodsId();
                if (consumeGoodsId.size() < goodsIndex) {
                    return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_ACTIVITY_PPL_ITEM_USE_ERROR, new Property());
                }
                Integer goodsId = consumeGoodsId.get(goodsIndex);
                if (goodsId == 0) {
                    consume = new Property();
                } else {
                    consume = new Property(goodsId, BigInteger.valueOf(needConsume));
                }
                if (!player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.Patrons, eLogMoneyType.PatronsSkillUpgrade)) {
                    return new SkillUpgradeDeductConsumeResult(GameErrorCode.E_BAG_ITEM_NO_ENOUGH, new Property());
                }
            }
            return new SkillUpgradeDeductConsumeResult(0, consume);
        }
    }

    /**
     * 检测该技能当前可以到达的最大等级,如果不大于0返回技能配置值
     */
    public int getMaxLevel(UserPatronsSkill userPatronsSkill, SkillInfo skillInfo, int type, int patronsId) {
        int skillId = skillInfo.getId();
        //取配置
        int maxLevel = skillInfo.getMaxLevel();
        //皮肤加成的最大等级
        if (ePatronsSkillType.Skin.getValue() == type) {
            long maxSkillLv = 0;
            SkinPatronsSkill skinPatronsSkill = SkinMgr.getSkinPatronsSkillUpgradeType(skillId);
            if (null != skinPatronsSkill) {
                int upgradeType = skinPatronsSkill.getSkillUpgradeLimitType();
                int skinLv = 0;
                UserSkin userSkin = player.getModule(SkinModule.class).getUserSkin(skinPatronsSkill.getSkinId());
                if (null != userSkin) {
                    skinLv = userSkin.getLevel();
                }
                //双生皮肤特殊处理
                SkinInfo twinsSkinInfo = SkinMgr.getTwinsSkinInfo(skinPatronsSkill.getSkinId());
                if (null != twinsSkinInfo) {
                    UserSkin userTwinsSkin = player.getModule(SkinModule.class).getUserSkin(twinsSkinInfo.getSkinId());
                    if (null != userTwinsSkin) {
                        skinLv += userTwinsSkin.getLevel();
                    }
                }
                SkillUpgradeInfo skillUpgradeInfo = SkillMgr.getSkillUpgradeInfoIfNotGetMax(upgradeType, skinLv);
                if (null != skillUpgradeInfo) {
                    maxSkillLv = skillUpgradeInfo.getConsumeCount();
                }
            }
            return (int) maxSkillLv;
        }
        // 蛐蛐虫师技能
        if (ePatronsSkillType.Cricket.getValue() == type) {
            // 根据技能id获取参数
            CricketConfig cricketConfig = CricketMgr.getCricketConfigByPartonSkill(skillId);
            if (cricketConfig != null) {
                String skillParam = CricketMgr.getPartonSkillParam(skillInfo.getStars(), StringUtils.string2intSafe(userPatronsSkill.getExtraParams(), 0));
                List<String> paramList = StringUtils.stringToStringList(skillParam, ";");
                int max = Integer.parseInt(paramList.get(0));
                //虫师技能等级上限加成
                int add = getCricketPatronsSkillAddition(userPatronsSkill);
                return max + add;
            }
        }

        //是否是门客信物的伴生技能
        if (ConfigMgr.getSakeCompanionParamMap().containsKey(skillInfo.getId())) {
            //看下门客有没有信物技能
            UserPatronsSkill sakeSkill = null;
            UserPatrons userPatrons = getUserPatrons(patronsId);
            for (UserPatronsSkill skill : userPatrons.getSkillList()) {
                SkillInfo ski = SkillMgr.getSkillInfo(skill.getSkillId());
                if (ski.getType() == eSkillType.PatronsKeepSakeLinkAddtition.getValue()) {
                    sakeSkill = skill;
                    break;
                }
            }
            if (sakeSkill != null) {
                maxLevel = ConfigMgr.getPatronsCompanionSkillLvLimit(skillInfo.getId(), sakeSkill.getSkillLv(), maxLevel);
                return maxLevel;
            }
        }

        //技能对指定技能列表 加等级上限
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsId);
        if (patronsInfo != null && patronsInfo.getInitSkillList().contains(skillId)) {
            maxLevel += getSkillMaxLevelAddValue();
        }
        //晋升解锁的技能也要加上限，如韩立，晋升类型5
        if (patronsInfo != null) {
            UserPatrons userPatrons = getUserPatrons(patronsId);
            PromotionInfo promotionInfo = PromotionMgr.getPromotionInfoById(userPatrons.getShowPromotionId());
            if(promotionInfo != null && promotionInfo.getType() == 5){
                if(patronsInfo.getSkillUnLockMap().containsKey(skillId)){
                    maxLevel += getSkillMaxLevelAddValue();
                }
            }
        }
        //鱼类可以加精进技能上线
        if (0 < maxLevel) {
            if (PatronsMgr.isStageSkill(skillId)) {
                maxLevel += FishPondMgr.getStageSkillMaxLevelAdd(player, eSkillType.FishPondStageSkillMaxLevelAdd.getValue());
                //厢房徽章加精进等级上限
                maxLevel += player.getModule(WingRoomModule.class).getStageSkillMaxLevelAdd();
                //药铺徽章加精进等级上限
                maxLevel += player.getModule(DrugStoreModule.class).getStageSkillMaxLevelAdd();
                // 蛐蛐徽章加精进等级上限
                maxLevel += player.getModule(CricketModule.class).getStageSkillMaxLevelAdd();
                //妙音坊加精进等级上限
                maxLevel += player.getModule(MusicRoomModule.class).getStageSkillMaxLevelAdd();
            }
            return maxLevel;
        }

        return maxLevel;    //无特殊情况返回技能配置的maxlevel

    }

    /**
     * 获取门客虫师技能等级上限加成
     * @param userPatronsSkill
     * @return
     */
    private int getCricketPatronsSkillAddition(UserPatronsSkill userPatronsSkill) {
        //门客配置信息
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatronsSkill.getPatronsId());
        int add = 0;
        //门客天赋加成
        add += this.getCricketPatronsTalentAddition(userPatronsSkill);
        //藏宝加成
        add += this.getCricketPatronsCurioAddition(patronsInfo.getOccupation());
        return add;
    }

    /**
     * 虫师技能加技能等级上限（藏宝加成）
     * @param occupation
     * @return
     */
    private int getCricketPatronsCurioAddition(int occupation) {
        return (int) CurioMgr.getSkillAdditionBySkillType(eSkillType.CurioAddCricketPatronsSkillMaxSkill.getValue(), player, occupation);
    }

    /**
     * 虫师技能加技能等级上限（门客天赋加成）
     */
    private int getCricketPatronsTalentAddition(UserPatronsSkill userPatronsSkill) {
        //门客（蛐蛐）虫师技能对应的职业，从蛐蛐表来
        int occp = -1;
        for (CricketConfig cfg : CricketMgr.getConfig().getCricketConfigMap().values()) {
            if (cfg.getPartonSkill() == userPatronsSkill.getSkillId()) {
                occp = cfg.getOccupation();
                break;
            }
        }
        if (occp == -1) {
            return 0;
        }

        //韩立或其他门客的天赋
        List<UserPatronsSkill> skillList = new ArrayList<>();
        for (UserPatrons patrons : userPatronsMap.values()) {
            for (UserPatronsSkill pSkill : patrons.getSkillList()) {
                if (pSkill.getType() != ePatronsSkillType.Talent.getValue()) {
                    continue;
                }
                SkillInfo pSkillInfo = SkillMgr.getSkillInfo(pSkill.getSkillId());
                if (pSkillInfo != null) {
                    if (pSkillInfo.getType() == eSkillType.PatronsCricketSkillLevelAddition.getValue()) {
                        if (occp == 0 || pSkillInfo.getParamList().get(2) == occp) {
                            skillList.add(pSkill);
                        }
                    }
                }
            }
        }
        int add = 0;
        for (UserPatronsSkill ups : skillList) {
            SkillInfo pSkillInfo = SkillMgr.getSkillInfo(ups.getSkillId());
            add += SkillMgr.getSkillAddition(pSkillInfo, ups.getSkillLv());
        }
        return add;
    }

    //通过maxlevel查看是否可升级
    private int checkSkillMaxLevel(int skillId, int upgradeType, int maxLevel, int skillLv, int times, int type, UserPatrons userPatrons) {
        //组合先去判断皮肤等级要求，再去判断等级
        if (ePatronsSkillType.PatronsSkinComb.getValue() == type) {
            SkinInfo skinInfo = SkinMgr.getSkinInfo(skillId, userPatrons.getPatronsId());
            UserSkin userSkin = player.getModule(SkinModule.class).getUserSkin(skinInfo.getSkinId());
            if (userSkin.getLevel() < SkillMgr.getSkillUpgradeInfo(upgradeType, skillLv + times).getConsumeCount()) {//皮肤等级限制
                return GameErrorCode.E_PATRONS_SKIN_LEVEL_NO_ENOUGH;
            }
        }

        if (-1 == maxLevel) {//需要小于门客当前等级
            if (skillLv >= userPatrons.getLevel()) {
                return GameErrorCode.E_PATRONS_LEVEL_NO_ENOUGH;
            }
            if (times != 1 && (skillLv + times - 1) >= userPatrons.getLevel()) {      //v4.5十连的最后一次升级需要等级大于门客当前等级
                return GameErrorCode.E_PATRONS_LEVEL_NO_ENOUGH;
            }
        } else if (-2 == maxLevel) {//需要小于门客进修等级加1
            if (skillLv >= userPatrons.getStageLv() + 1) {
                return GameErrorCode.E_PATRONS_STAGE_LEVEL_NO_ENOUGH;
            }
            if (times != 1 && (skillLv + times - 1) >= userPatrons.getStageLv() + 1) {
                return GameErrorCode.E_PATRONS_STAGE_LEVEL_NO_ENOUGH;
            }
        } else if (-4 == maxLevel) {//不可升级
            return GameErrorCode.E_PATRONS_SKILL_NO_UPGRADE;
        } else if (0 == maxLevel) {//无限制

        } else if (0 < maxLevel) {
            if (skillLv >= maxLevel) {
                return GameErrorCode.E_PATRONS_SKILL_LEVEL_MAX;
            }
        } else {
            return GameErrorCode.E_PATRONS_SKILL_TYPE_ERROR;
        }


        return 0;
    }

    /**
     * 组合技能查看组合是否解锁
     */
    private int checkCombSkill(int type, int skillId, int patronsId) {
        if (ePatronsSkillType.Comb.getValue() == type) {//如果是组合技能，判断组合是否激活解锁
            if (!PatronsMgr.isUnlockPatronsComb(patronsId, userPatronsMap)) {
                return GameErrorCode.E_PATRONS_COMB_IS_LOCK;
            }
        } else if (ePatronsSkillType.PatronsSkinComb.getValue() == type) {
            SkinInfo skinInfo = SkinMgr.getSkinInfo(skillId, patronsId);
            if (null == skinInfo) {
                return GameErrorCode.E_PATRONS_SKIN_COMB_SKILL_IS_LOCK;
            }
            if (!SkinMgr.isUnlockPatronsSkinComb(skinInfo.getSkinId(), player.getModule(SkinModule.class).getUserSkinMap())) {
                return GameErrorCode.E_PATRONS_SKIN_COMB_SKILL_IS_LOCK;
            }
        }
        return 0;
    }

    /**
     * 查看是不是秦始皇的俩特殊门客技能,是的话特殊处理
     */
    private int checkQinEmperorSkill(int skillId, int patronsId, int upgradeType, int skillLv, int times) {

        //秦始皇排面 一个门客技能拉出来看其他门客技能等级
        if (skillId == 10631) {         //魔法值 秦始皇技能千古一帝  eSkillType.XiaoXiongPatronHalo 后面通用的话改成类型判断
            int totalSkillLevel = 0;      //总技能等级
            CombInfo patronsComb = PatronsMgr.getPatronsComb(10010);    //从一世枭雄的组合里拿每个人的光环技能
            Map<Integer, Integer> patronsIdSkillMap = patronsComb.getPatronsIdSkillMap();
            for (Map.Entry<Integer, Integer> entry : patronsIdSkillMap.entrySet()) {       //遍历每个组合内门客
                if (entry.getKey() == patronsId) {
                    continue;
                }
                if (userPatronsMap.containsKey(entry.getKey())) {        //如果有这个门客 看他的这个光环技能
                    List<UserPatronsSkill> userSkillList = userPatronsMap.get(entry.getKey()).getSkillList();
                    for (UserPatronsSkill userPatronsSkill : userSkillList) {
                        if (userPatronsSkill.getSkillId() == entry.getValue()) {
                            totalSkillLevel += userPatronsSkill.getTotalSkillLevel();
                            break;
                        }
                    }
                }
            }
            SkillUpgradeInfo thisGradeUpdateInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, skillLv + times - 1);  //查看升级需求
            if (totalSkillLevel < thisGradeUpdateInfo.getConsumeCount()) {
                return GameErrorCode.E_PATRONS_COMB_ALL_SKILL_LEVEL_NO_ENOUGH;
            }

        }

        return 0;
    }

    //类似159技能的，同一种类型的门客技能等级决定这个技能的升级，奇人异事门客的技能来加起来，秦始皇的一代枭雄一样
    private int checkOtherSkillAddThisSkillSkill(int skillId, int patronsId, int upgradeType, int skillLv, int times) {
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsId);
        SkillInfo skillInfo = SkillMgr.getSkillInfo(skillId);
        if (skillInfo.getType() == eSkillType.StrangePatronHalo.getValue()) {
            int totalSkillLevel = 0;      //总技能等级
            CombInfo patronsComb = PatronsMgr.getPatronsComb(patronsInfo.getCombId());    //从这个门客的对应的同类光环门客中
            Map<Integer, Integer> patronsIdSkillMap = patronsComb.getPatronsIdSkillMap();
            for (Map.Entry<Integer, Integer> entry : patronsIdSkillMap.entrySet()) {       //遍历每个组合内门客
                if (entry.getKey() == patronsId) {
                    continue;
                }
                if (userPatronsMap.containsKey(entry.getKey())) {        //如果有这个门客 看他的这个光环技能
                    List<UserPatronsSkill> userSkillList = userPatronsMap.get(entry.getKey()).getSkillList();
                    for (UserPatronsSkill userPatronsSkill : userSkillList) {
                        if (userPatronsSkill.getSkillId() == entry.getValue()) {
                            totalSkillLevel += userPatronsSkill.getTotalSkillLevel();
                            break;
                        }
                    }
                }
            }
            SkillUpgradeInfo thisGradeUpdateInfo = SkillMgr.getSkillUpgradeInfo(upgradeType, skillLv + times - 1);  //查看升级需求
            if (thisGradeUpdateInfo == null || totalSkillLevel < thisGradeUpdateInfo.getConsumeCount()) {
                return GameErrorCode.E_PATRONS_COMB_ALL_SKILL_LEVEL_NO_ENOUGH;
            }
        }
        return 0;
    }

    /**
     * 组合光环等级:检测光环技能等级是否符合升级
     * @param skillInfo
     * @param userPatrons
     * @param skillLv
     * @param goodsIndex
     * @param times
     * @return
     */
    private int checkComSkillDeMaxLevelSkill(SkillInfo skillInfo, UserPatrons userPatrons, int skillLv, int goodsIndex, int times) {
        int skillId = skillInfo.getId();
        int patronsId = userPatrons.getPatronsId();
        int quality = PatronsMgr.getPatronsQuality(userPatrons);//品质
        int consumeId = skillInfo.getConsumeGoodsId().get(goodsIndex);
        String key = patronsId + "#" + skillId;
        List<PatronsSkillParam> patronsSkillParamList = GameConfig.PATRONS_SKILL_LEVEL_SUM_DEPEND_PARAM_MAP.get(key);
        //判断是否在技能池子中
        if (patronsSkillParamList == null || patronsSkillParamList.isEmpty()) {
            return 0;
        }
        //判断技能是否达到最大等级
        Map<Integer, Integer> patronsSkillLimitParamMap = GameConfig.PATRONS_SKILL_LIMIT_PARAM_MAP.get(patronsId + "#" + quality);
        if (patronsSkillLimitParamMap == null) {
            return GameErrorCode.E_PATRONS_SKILL_NO_UPGRADE;
        }
        Integer maxLevel = patronsSkillLimitParamMap.get(skillId);
        if (maxLevel == null || (skillLv + times) > maxLevel) {
            return GameErrorCode.E_PATRONS_SKILL_NO_UPGRADE;
        }
        //获取取决的技能等级之和
        int totalSkillLevel = 0;//总技能等级
        for (PatronsSkillParam patronsSkillParam : patronsSkillParamList) {
            UserPatrons checkUserPatrons = this.getUserPatrons(patronsSkillParam.getPatronsId());
            if (checkUserPatrons == null) {
                continue;
            }
            UserPatronsSkill checkUserPatronsSkill = checkUserPatrons.getPatronsSkill(patronsSkillParam.getSkillId());
            if (checkUserPatronsSkill == null) {
                continue;
            }
            totalSkillLevel += checkUserPatronsSkill.getTotalSkillLevel();
        }
        //升级需求
        Property consume = SkillMgr.calcSkillLvTotalConsume(skillId, 0, skillLv + times, goodsIndex);
        long consumeNum = consume.getCountByGoodsId(consumeId).longValue();
        if (consume.isNothing() || totalSkillLevel < consumeNum) {
            return GameErrorCode.E_PATRONS_COMB_ALL_SKILL_LEVEL_NO_ENOUGH;
        }
        return 0;
    }

    public void addBaiyeExp(int patronsId, long value) {
        UserPatrons patrons = getUserPatrons(patronsId);
        if (patrons != null) {
            patrons.setBaiYeExp(patrons.getBaiYeExp() + value);
            player.getModule(PatronsModule.class).syncPatronsData(patrons);
        }
    }

    public void addChousuanExp(int patronsId, long value) {
        UserPatrons patrons = getUserPatrons(patronsId);
        if (patrons != null) {
            patrons.setChouSuanExp(patrons.getChouSuanExp() + value);
            player.getModule(PatronsModule.class).syncPatronsData(patrons);
        }
    }

    /**
     * 装备座骑
     */
    public int equipVehicle(int patronsId, String vehicleUid) {
        UserPatrons userPatrons = userPatronsMap.get(patronsId);
        if (null == userPatrons) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }

        if (StringUtils.isNullOrEmpty(vehicleUid)) {//卸座骑

            UserVehicle userVehicle = player.getModule(VehicleModule.class).getUserVehicleByPatrons(patronsId);
            if (null == userVehicle) {
                return GameErrorCode.E_PATRONS_NO_EQUIP_VEHICLE;
            }
            player.getModule(VehicleModule.class).dismountVehicle(userVehicle);
            onPatronsChange(userPatrons);
            return 0;
        } else {
            UserVehicle userVehicle = player.getModule(VehicleModule.class).getUserVehicle(vehicleUid);
            if (!VehicleMgr.isUserVehicleExist(userVehicle)) {
                return GameErrorCode.E_VEHICLE_NO_FOUND;
            }

            if (!player.getModule(VehicleModule.class).canMountVehicle(vehicleUid, patronsId)) {
                return GameErrorCode.E_PATIONS_CAN_NOT_DISMOUNT_VEHICLE_IN_ACTIVITY;
            }

            UserVehicle patronsVehicle = player.getModule(VehicleModule.class).getUserVehicleByPatrons(patronsId);

            //是否允许装备
            if (!UnionPhaseWarMgr.canMountVehicle(player, vehicleUid, patronsId)) {
                return GameErrorCode.E_UNION_WAR_ACTIVITY_VEHICLE_IN_WAR_NOT_MOUNT;
            }

            //超过免费装备次数，需要消耗
            if (userVehicle.getEquipTimes() >= GameConfig.VEHICLE_DEFAULT_EQUIP_TIMES) {
                int ingots = VehicleMgr.getVehicleEquipCost(userVehicle.getEquipBuyTimes());
                if (!player.getModule(CurrencyModule.class).removeCurrency(new Property(GameConfig.GAME_MONEY_INGOTS, BigInteger.valueOf(ingots)),
                        eLogMoneyType.Vehicle, eLogMoneyType.VehicleEquip)) {
                    return GameErrorCode.E_BAG_INGOTS_NO_ENOUGH;
                }
                userVehicle.setEquipBuyTimes(userVehicle.getEquipBuyTimes() + 1);
            }
            if (userVehicle.getPatronsId() > 0) {//已经装备座骑了，先卸下
                int tempPatronsId = userVehicle.getPatronsId();
                player.getModule(VehicleModule.class).dismountVehicle(userVehicle);
                UserPatrons tempPatrons = getUserPatrons(tempPatronsId);
                if (null != tempPatrons) {
                    onPatronsChange(tempPatrons);
                }
            }
            //商会战活动
            if (null != patronsVehicle) {
                UnionPhaseWarMgr.dismountVehicle(player, patronsId, patronsVehicle.getVehicleUid());

                // 日志
                AutoLogMgr.add(new LogVehicleEquip(
                        player.getUserId(),
                        userVehicle.getVehicleUid(),
                        userVehicle.getTemplateId(),
                        patronsId,
                        0
                ));
            }
            player.getModule(VehicleModule.class).equipVehicle(vehicleUid, patronsId);
            try {
                player.getModule(RankUnionWarModule.class).equipVehicleOrFish(patronsId);
            } catch (Exception e) {
                log.error("RankUnionWarModule equipVehicleOrFish error:", e);
            }
            onPatronsChange(userPatrons);
            return 0;
        }
    }

    /**
     * 获取某职业的门客，传入参数为0，则纯随机
     */
    public UserPatrons getRandomPatrons(int occupation) {
        List<UserPatrons> dataList = new ArrayList<>();
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            if (patronsInfo.getOccupation() == occupation || 0 == occupation) {
                dataList.add(userPatrons);
            }
        }
        if (dataList.size() > 0) {
            int randomValue = threadSafeRandom.next(0, dataList.size());
            return dataList.get(randomValue);
        }
        return null;
    }

    public UserPatrons getFirstIndexPatrons() {
        List<UserPatrons> dataList = new ArrayList<>(userPatronsMap.values());
        if (dataList.size() > 0) {
            return dataList.get(0);
        }
        return null;
    }

    /**
     * 门客属性变化
     */
    public void onPatronsChange(UserPatrons userPatrons) {
        List<UserPatrons> patronsList = new ArrayList<>();
        if (null != userPatrons) {
            patronsList.add(userPatrons);
        } else {//不指定门客，则全部刷新门客实力
            patronsList.addAll(userPatronsMap.values());
        }
        this.onPatronsChangeByList(patronsList);
    }

    /**
     * 门客属性变化
     */
    public void onPatronsChangeByList(List<UserPatrons> patronsList) {
        onPatronsListChange(patronsList);
        //能力计算后再通知
        for (UserPatrons patrons : patronsList) {
            notifyPatronsDispatch(patrons);
        }
    }

    /**
     * 狩猎期间，同步议事厅门客能力
     */
    private void notifyPatronsDispatch(UserPatrons userPatrons) {
        if (userPatrons == null) {
            return;
        }
        //议事厅门客能力变更
        if (!HuntConfigMgr.isHuntSystemOpenTime()) {
            return;
        }
        if (!HuntConfigMgr.isInHuntBattleTime()) {
            return;
        }
        if (StringUtils.isNullOrEmpty(player.getUserInfo().getUnionUid())) {
            return;
        }
        CrossUnionMember member = CrossUnionMgr.getUnionMember(player.getUserInfo().getUnionUid(), player.getUserId());
        if (member != null) {
            if (userPatrons.getPatronsId() == member.getChamberPatronsId()) {
                CrossUnionMgr.userPatronDispatchSkillChangeNotify(member.getUnionUid(), player.getUserId(), userPatrons);
            }
        }
    }

    /**
     * 门客集合属性变化
     */
    public void onPatronsListChange(List<UserPatrons> patronsList) {
        if (null == patronsList) {
            return;
        }
        CalcPatronsAbilityParam calcPatronsAbilityParam = buildCalcPatronsAbilityParam();

        // 热更：记录赚钱变化的门客
        List<UserPatrons> abilityChangeList = new ArrayList<>();//这个给巅峰商会战用,赚钱没变也往里面塞,不懂
        List<UserPatrons> realAbilityChangeList = new ArrayList<>();//这个是实际赚钱有变动的

        List<UserPatrons> qualificationChangeList = new ArrayList<>();

        for (UserPatrons patrons : patronsList) {//重新计算门客资质、实力，并缓存

            BigInteger oldAbility = patrons.getAbility();

            long oldQualification = patrons.getQualification();

            PatronsMgr.getPatronsAbility(patrons, calcPatronsAbilityParam);

            if (patrons.getQualification() != oldQualification) {
                qualificationChangeList.add(patrons);
            }

            if (patrons.getAbility().compareTo(oldAbility) != 0) {
                abilityChangeList.add(patrons);

                realAbilityChangeList.add(patrons);

                //更新美女的缘分加成
                BeautyModule beautyModule = player.getModule(BeautyModule.class);
                List<UserBeauty> patronsRelationBeauty = beautyModule.getPatronsRelationBeauty(patrons);
                for (UserBeauty userBeauty : patronsRelationBeauty) {
                    beautyModule.reCalcBeautyTotalRelationPatronsAddAbility(userBeauty, calcPatronsAbilityParam);
                }
            }

            //门客变更能力，需要同步
            PatronsSimpleCacheMgr.changeUserPatronsSimple(patrons);

            player.getModule(PeakStrongholdModule.class).checkStrongholdTopChange(patrons);
        }

        // 热更：特殊情况 这个特殊情况已经不知道特殊在哪里要不要去掉了
        if (abilityChangeList.isEmpty() && patronsList.size() == 1) {
            abilityChangeList.add(patronsList.get(0));
        }

        player.getModule(EarnSpeedModule.class).scheduleAddSilver();
        BigInteger totalPatronsAbility = getTotalPatronsAbility();
        long totalRiseQualification = getTotalRiseQualification();
        // 修改userInfo
        player.getUserInfo().setTotalAbility(totalPatronsAbility);
        //记录变化
        UserHistoryAttribute userHistoryAttribute = player.getModule(PlayerModule.class).getUserHistoryAttribute();
        if (totalPatronsAbility.compareTo(userHistoryAttribute.getAbility()) > 0) {
            userHistoryAttribute.setAbility(totalPatronsAbility);
            updateHistoryMaxEarnSpeed.set(true);

            //巅峰据点战报名期要更新门客历史最高赚力
//                PeakStrongholdActivityMgr.addWaitTakeAbilityMembers(getUserId(), player.getUserInfo().getUnionUid());

            //同步到商会跨服
            CrossUnionMemberAbilitySyncTask.addWaitUpdateAbilityUser(getUserId());
        }
        if (totalRiseQualification > userHistoryAttribute.getQualification()) {
            userHistoryAttribute.setQualification(totalRiseQualification);
        }
        RankMgr.changeUserRank(eBigRankType.Normal.getValue(), player.getUserId(), totalPatronsAbility, eRankType.PatronsAbility.getValue(), "");
        //乔迁风云中能力更新
        player.getModule(EstateWarModule.class).userInfoChange(true);
        //抢夺资源中能力更新
        player.getModule(MineWarModule.class).patronsAbilityChange(patronsList);
        //抢夺资源中能力更新
        player.getModule(OreWarModule.class).patronsAbilityChangeAdd(patronsList);

        player.getModule(StrongholdModule.class).patronsAbilityChange(patronsList);

        //成就、任务
        player.notifyListener(eGamePlayerEventType.PatronsTotalQualification.getValue(), new PatronsArgs(userPatronsMap));
        player.notifyListener(eGamePlayerEventType.PatronsTotalAbility.getValue(), new PatronsArgs(userPatronsMap));
        player.notifyListener(eGamePlayerEventType.PatronsTotalEarnSpeed.getValue(), totalPatronsAbility);
        //资质、实力提升冲榜
        player.notifyListener(eGamePlayerEventType.QualificationRiseRank.getValue(), totalRiseQualification);
        player.notifyListener(eGamePlayerEventType.AbilityRiseRank.getValue(), totalPatronsAbility);
        player.notifyListener(eGamePlayerEventType.UnionAbilityRiseRank.getValue(), new UnionRiseRankArgs(UnionRiseRankArgs.NOTIFY_TYPE_NORMAL, totalPatronsAbility, player.getUserInfo().getUnionUid()));
        //同步即时实力
        player.sendPacket(Protocol.U_ABILITY_SYNC, PlayerBasePb.parseAbilityTempMsg(totalPatronsAbility));

        try {
            for (UserPatrons patrons : patronsList) {//重新计算门客资质、实力，并缓存
                SkinModule skinModule = player.getModule(SkinModule.class);
                for (int skinId : SkinMgr.getSkinListByRoleId(eSkinType.Patrons, patrons.getPatronsId())) {
                    UserSkin userSkin = skinModule.getUserSkinMap().get(skinId);
                    if (userSkin != null) {
                        JSONObject jsonObject = new JSONObject();
                        UserPatronsSkill userPatronsSkill = patrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), SkinMgr.getSkinInfoMap().get(skinId).getSkinEffect().get(0));
                        jsonObject.put("skillLevel", userPatronsSkill.getSkillLv() + "");
                        jsonObject.put("value", patrons.getAbility().toString());
                        CombInfo patronsSkinComb = SkinMgr.getPatronsSkinComb(SkinMgr.getSkinInfoMap().get(skinId).getCombId());
                        if (null != patronsSkinComb) {
                            UserPatronsSkill combSkill = patrons.getPatronsSkill(ePatronsSkillType.PatronsSkinComb.getValue(), patronsSkinComb.getCombSkillId());
                            if (null != combSkill) {
                                jsonObject.put("skinCombLv", String.valueOf(combSkill.getTotalSkillLevel()));//组合皮肤comb技能等级
                            }
                            for (int tempSkinId : patronsSkinComb.getObjectIdList()) {
                                SkinInfo tempSkin = SkinMgr.getSkinInfo(tempSkinId);
                                if (tempSkin.getBelongRole() == patrons.getPatronsId()) {
                                    UserPatronsSkill skill = patrons.getPatronsSkill(ePatronsSkillType.PatronsSkinComb.getValue(), patronsSkinComb.getSkillId(skinId));
                                    if (skill != null) {
                                        jsonObject.put("skinCombSkillLv", String.valueOf(skill.getSkillLv()));//组合皮肤普通技能等级
                                    } else {
                                        jsonObject.put("skinCombSkillLv", "0");//组合皮肤普通技能等级
                                    }
                                }
                            }
                        }
                        //xx 组合门客新加展示
                        int rankType = PatronsMgr.getPatronsSkinRankRankType(skinId);//获取门客皮皮肤rankType
                        RankMgr.changeUserRank(eBigRankType.Skin.getValue(), player.getUserId(), BigInteger.valueOf(userSkin.getLevel()), rankType, jsonObject.toJSONString());
                    }
                }
                patronsRankChange(patrons);
            }
        } catch (Exception ex) {
            log.error(ex);
        }
        //商会战
        UnionPhaseWarMgr.patronsAbilityChange(player.getUserInfo(), patronsList, getUserPatronsMap());
        //巅峰商会战
        if (abilityChangeList.size() > 0) {
            RankUnionWarMgr.patronsAbilityChange(player.getUserInfo(), abilityChangeList);
        }
        if (realAbilityChangeList.size() > 0) {
            syncPatronsCache(realAbilityChangeList);
        }
        // 榷场
        MonopolyMarketMgr.synPatronsAbility(player, patronsList);
        NewMonopolyMarketMgr.synPatronsAbility(player, patronsList);
        SeacraftSouthSeaMgr.synPatronsAbility(player, patronsList);
        PeakSeacraftMgr.synPatronsAbility(player, patronsList);

        try {
            //绝代双骄
            player.getModule(TwinsModule.class).patronsAbilityChange();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        // 灯火街市
        player.getModule(JieShiModule.class).patronsAbilityChange(patronsList);

        // 矿场
        player.getModule(KuangModule.class).patronsAbilityChange();

        // 九州棋局
        JiuZhouMgr.addPatronsUploadAbility(player.getUserId(), patronsList, false);

        DouLuoDaLuMgr.addNeedUploadUserInfo(player);
        player.getModule(MatchModule.class).syncPatronsAbility(patronsList);
        //皇宫同步实力
        PalaceActivity palaceActivity = PalaceActivityMgr.getInTimePalaceActivity();
        if (null != palaceActivity) {
            palaceActivity.syncUserAbility(player.getUserId(), totalPatronsAbility);
        }
        //新门客基金-门客资质变化
        player.getModule(PatronsFundModule.class).qualificationChange(qualificationChangeList);
        //新门客基金最新版-门客资质变化
        for (UserPatrons userPatrons : qualificationChangeList) {
            player.notifyListener(eGamePlayerEventType.NewPatronsFundPatronsQua.getValue(), 0);
        }

        reCalKeepSakeQualification(qualificationChangeList);
    }

    private void reCalKeepSakeQualification(List<UserPatrons> qualificationChangeList) {
        try {
            Set<Integer> toCalPatronsId = new HashSet<>();
            if (qualificationChangeList.size() > 0) {
                Iterator<UserPatrons> iterator = qualificationChangeList.iterator();
                while (iterator.hasNext()) {
                    UserPatrons userPatrons = iterator.next();
                    for (Map.Entry<Integer, UserPatrons> entry : getUserPatronsMap().entrySet()) {
                        UserPatrons relationPatrons = entry.getValue();
                        if (relationPatrons.getPatronsId() == userPatrons.getPatronsId()) {
                            continue;
                        }
                        if (userPatrons.getShowKeepSakePatronsId() == userPatrons.getPatronsId()) {
                            toCalPatronsId.add(relationPatrons.getPatronsId());
                        }
                    }
                }
            }
            if (toCalPatronsId.size() > 0) {
                List<UserPatrons> toCalList = new ArrayList<>();
                for (int patronsId : toCalPatronsId) {
                    UserPatrons userPatrons = getUserPatrons(patronsId);
                    if (userPatrons != null) {
                        toCalList.add(userPatrons);
                    }
                }
                if (toCalList.size() > 0) {
                    onPatronsListChange(toCalList);
                }
            }
        } catch (Exception e) {

        }
    }

    /**
     * zfr 构造门客赚钱值计算参数
     * @return CalcPatronsAbilityParam
     */
    public CalcPatronsAbilityParam buildCalcPatronsAbilityParam() {
        CalcPatronsAbilityParam.CalcPatronsAbilityParamBuilder builder = CalcPatronsAbilityParam.CalcPatronsAbilityParamBuilder.aCalcPatronsAbilityParam();
        builder.withUserSkinMap(player.getModule(SkinModule.class).getUserSkinMap())
                .withPatronsMap(player.getModule(PatronsModule.class).getUserPatronsMap())
                .withVehicleMap(player.getModule(VehicleModule.class).getUserVehicleMap())
                .withBeautyMap(player.getModule(BeautyModule.class).getBeautyMap())
                .withUserDishesMap(player.getModule(InnModule.class).getUserDishesMap())
                .withManorTechnologyDataMap(player.getModule(ManorModule.class).getTechnologyDataMap())
                .withEmbroideryMeter(player.getModule(SkinVolumeModule.class).getSkinVolumeData().getEmbroideryMeter())
                .withUserLTPMainRoleSkinMap(player.getModule(SkinModule.class).getUserLTPMainRoleSkinMap())
                .withFishDataMap(player.getModule(ManorModule.class).getFishDataMap())
                .withGrandChildProfessionSkillAdditionMap(player.getModule(GrandChildModule.class).getGrandChildProfessionAddAbilityMap())
                .withUserAssistantSuitMap(player.getModule(AssistantSkinModule.class).getAssistantSuitMap())
                .withFurnitureMap(player.getModule(WingRoomModule.class).getFurnitureMap())
                .withSuitDataMap(player.getModule(WingRoomModule.class).getSuitDataMap())
                .withWineryItemInfoMap(player.getModule(WineryModule.class).getUserWineryItemInfoMap())
                .withCricketDataMap(player.getModule(CricketModule.class).getCricketDataMap())
                .withUserPatronsSkinBondDataMap(player.getModule(SkinModule.class).getUserPatronsSkinBondMap())
                .withUserVehicleSoulMap(player.getModule(VehicleSoulModule.class).getUserVehicleSoulMap())
                .withUserVehicleBoneMap(player.getModule(VehicleBoneModule.class).getAllEquipBoneMap())
                .withWingRoomChakraAbilityAddMap(WingRoomChakraMgr.getPatronsAddValueMap(player.getUserId(), WingRoomPositionTypeEnum.POSITION_TYPE_SX))
                .withWingRoomChakraQualificationAddMap(WingRoomChakraMgr.getPatronsAddValueMap(player.getUserId(), WingRoomPositionTypeEnum.POSITION_TYPE_MG))
                .withCurioSkillDataMap(player.getModule(CurioModule.class).getSkillDataMap())
                .withCombLeaderDataMap(player.getModule(CombLeaderModule.class).getLeaderDataMap())
                .withTravelBookBeautyMap(player.getModule(TravelBookModule.class).getBeautyAlbumMap());
        return builder.build();
    }

    /**
     * 每日重置次数
     */
    public void resetOneDay() {

    }

    /**
     * 取得所有门客赚钱能力
     */
    public BigInteger getTotalPatronsAbility() {
        BigInteger ability = BigInteger.ZERO;
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            ability = ability.add(userPatrons.getAbility());
        }
        return ability;
    }

    /**
     * 获取总资质
     */
    public long getTotalQualification() {
        long qualification = 0;
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            qualification += userPatrons.getTotalQualification();
        }
        return qualification;
    }

    /**
     * 获取总提升资质，不包括门客自身资质
     */
    public long getTotalRiseQualification() {
        long qualification = 0;
        Map<Integer, UserCombLeaderData> leaderDataMap = player.getModule(CombLeaderModule.class).getLeaderDataMap();
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            long temp = userPatrons.getTotalQualification();
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            //晋升另外算
            int promotionId = userPatrons.getShowPromotionId();
            if (promotionId != 0) {
                if (null != PromotionMgr.getPromotionInfoById(promotionId)) {
                    temp = temp - Long.parseLong(PromotionMgr.getPromotionInfoById(promotionId).getDefaultAttribute());
                }
            } else {
                temp = temp - patronsInfo.getQualification();
            }
            if (temp < 0) {
                temp = 0;
            }
            qualification += temp;
        }
        return qualification;
    }

    /**
     * 取得门客总赚钱能力
     */
    public BigInteger getPatronsListAbility(List<Integer> patronsIdList) {
        BigInteger ability = BigInteger.ZERO;
        for (int patronsId : patronsIdList) {
            UserPatrons userPatrons = getUserPatrons(patronsId);
            if (null != userPatrons) {
                ability = ability.add(userPatrons.getAbility());
            }
        }
        return ability;
    }

    public int getTalentSkillAddition(int skillType, int param) {
        return SkillMgr.getSkillAdditionFromMap(skillType, param, talentSkillAdditionMap);
    }

    /**
     * 获得商战分数天赋技能
     * @param skillType
     * @param patronsIdList
     * @return
     */
    public int getTalentTradeWarScoreSkillAddition(int skillType, List<Integer> patronsIdList) {
        int addition = 0;
        for (int patronsId : patronsIdList) {
            UserPatrons userPatrons = getUserPatrons(patronsId);
            if (null == userPatrons) {
                continue;
            }
            for (UserPatronsSkill patronsSkill : userPatrons.getSkillList()) {
                if (patronsSkill.getType() != ePatronsSkillType.Talent.getValue()) {
                    continue;
                }
                SkillInfo skillInfo = SkillMgr.getSkillInfo(patronsSkill.getSkillId());
                if (skillInfo.getType() == skillType) {
                    addition += SkillMgr.getSkillAddition(skillInfo, patronsSkill.getSkillLv());
                }
            }
        }
        return addition;
    }


    //检验列表中的门客是否合法
    public boolean checkPatronsList(List<Integer> patronList) {
        List<Integer> addPatronIdList = new ArrayList<>();//用于保存当前出场门客名单
        for (Integer patronId : patronList) {
            if (!userPatronsMap.containsKey(patronId)) {
                return false;
            }
            if (addPatronIdList.contains(patronId)) {
                return false;
            }
            addPatronIdList.add(patronId);
        }
        return true;
    }

    //获取派遣的特殊数据
    public Map<Integer, List<Integer>> getDispatchList(List<Integer> patronIdList) {
        Map<Integer, List<Integer>> occupationQualityMap = new ConcurrentHashMap<>();
        //所有职业的
        occupationQualityMap.put(0, new ArrayList<>());
        for (Integer patronId : patronIdList) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronId);
            if (patronsInfo == null) continue;
            if (!occupationQualityMap.containsKey(patronsInfo.getOccupation())) {
                occupationQualityMap.put(patronsInfo.getOccupation(), new ArrayList<>());
            }
            occupationQualityMap.get(patronsInfo.getOccupation()).add(patronsInfo.getQuality());
            occupationQualityMap.get(0).add(patronsInfo.getQuality());
        }
        return occupationQualityMap;
    }

    /**
     * 门客穿戴皮肤
     * @param patronsId
     * @param skinId
     * @return
     */
    public int putOnSkin(int patronsId, int skinId, boolean isLinkageSwitch) {
        // 判断美女是否的存在
        UserPatrons userPatrons = userPatronsMap.get(patronsId);
        if (userPatrons == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }

        // skinId = 0表示脱衣服
        if (skinId == 0) {
            if (isLinkageSwitch) {//只有联动的门客才能切换
                if (!PatronsMgr.isPatronsTwins(patronsId)) {
                    return GameErrorCode.E_PATRONS_NO_TWINS;
                }
                if (1 != player.getModule(GoodsResourceModule.class).getPatronsLinkType(patronsId)) {
                    return GameErrorCode.E_PATRONS_NO_LINKAGE_TYPE;
                }
            }
            userPatrons.setSkinId(0);
            userPatrons.setLinkageSwitch(isLinkageSwitch);
            syncPatronsData(userPatrons);
            player.getModule(OreWarModule.class).patronsAbilityChangeAdd(Arrays.asList(userPatrons));
            player.getModule(StrongholdModule.class).patronsAbilityChange(Collections.singletonList(userPatrons));
            return 0;
        }

        // 判断是否有这个皮肤
        if (!player.getModule(SkinModule.class).isSkinExist(skinId)) {
            return GameErrorCode.E_SKIN_IS_NOT_UNLOCK;
        }

        // 判断皮肤配置
        SkinInfo skinInfo = SkinMgr.getSkinInfoMap().get(skinId);
        if (skinInfo == null) {
            return GameErrorCode.E_SKIN_NOT_EXIST;
        }

        // 判断皮肤类型
        if (eSkinType.Patrons.getValue() != skinInfo.getType()) {
            return GameErrorCode.E_SKIN_TYPE_ERROR;
        }

        // 判断美女皮肤归属
        if (skinInfo.getBelongRole() != userPatrons.getPatronsId()) {
            return GameErrorCode.E_SKIN_IS_NOT_BELONG_TO_PATRONS;
        }

        userPatrons.setSkinId(skinId);
        userPatrons.setLinkageSwitch(isLinkageSwitch);
        syncPatronsData(userPatrons);
        player.getModule(OreWarModule.class).patronsAbilityChangeAdd(Arrays.asList(userPatrons));
        player.getModule(StrongholdModule.class).patronsAbilityChange(Collections.singletonList(userPatrons));
        return 0;
    }


    /**
     * 更新门客技能
     */
    public void flushPatronsSkill() {
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            initSkill(userPatrons, patronsInfo);
        }

        //推送厨艺值技能
        if (null != userPatronsMap) {
            syncPatronsData(new ArrayList<>(userPatronsMap.values()), true);
            onPatronsChange(null);
        }
    }

    /**
     * 橙色门客数量
     * @return
     */
    public int getOrangePatronsCount() {
        int count = 0;
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            if (patronsInfo.getQuality() == eQualityType.Orange.getValue()) {
                count++;
            }
        }
        return count;
    }

    /**
     * 增加百业经验
     * @param patronsId
     */
    public void addBaiYeExp(int patronsId, long addExp) {
        UserPatrons patrons = player.getModule(PatronsModule.class).getUserPatrons(patronsId);
        if (patrons != null) {
            patrons.setBaiYeExp(patrons.getBaiYeExp() + addExp);
            player.getModule(PatronsModule.class).syncPatronsData(patrons);
        }
    }

    //这里的逻辑拜访的也用到，改这个拜访也要去改
    public void patronsRankChange(UserPatrons userPatrons) {
        try {
            if (userPatrons != null) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("patronsId", userPatrons.getPatronsId());
                jsonObject.put("skinId", userPatrons.getSkinId());
                jsonObject.put("promotionId", userPatrons.getShowPromotionId());
                RankMgr.changeUserRank(eBigRankType.Patrons.getValue(), player.getUserId(), userPatrons.getAbility(), 0, jsonObject.toJSONString());
                //7.4.0 获取一下rankType
                int rankType = PatronsMgr.getPatronsRankRankType(userPatrons.getPatronsId());
                RankMgr.changeUserRank(eBigRankType.Patrons.getValue(), player.getUserId(), userPatrons.getAbility(), rankType, jsonObject.toJSONString());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 门客晋升
     */

    public int patronsPromote(int patronsId) {
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (null == userPatrons) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsId);
        if (patronsInfo == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        //取得当前门客晋升信息表
        List<PromotionInfo> promotionInfoList = PromotionMgr.getCharacterPromotion(patronsId);
        //取得拥有皮肤信息
        List<Integer> patronSkinIdList = player.getModule(SkinModule.class).getPatronsSkinIdList(patronsId);
        List<Integer> allSkinIdList = player.getModule(SkinModule.class).getSkinIdList(eSkinType.Patrons);

        //老技能天赋表
        List<Integer> oldSkillList = new ArrayList<>();
        List<Integer> oldTalentList = new ArrayList<>();

        //寻找配置表中新老技能表，天赋
        Long oldQualification = 0L;
        if (null == promotionInfoList) {
            return GameErrorCode.E_PATRONS_CAN_NOT_PROMOTE;
        }
        PromotionInfo promotionInfo = new PromotionInfo();

        //晋升id为0就取patronsMgr内的技能和天赋，否则去promotionMgr取
        if (0 == userPatrons.getPromotionId()) {
            PatronsInfo oldPatronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            oldQualification = Long.valueOf(oldPatronsInfo.getQualification());         //老天赋
            oldSkillList = oldPatronsInfo.getSkillList();                               //老技能
            oldTalentList = oldPatronsInfo.getTalentList();                             //老天赋
            promotionInfo = promotionInfoList.get(0);                                   //晋升信息为晋升表第一条
        } else {
            //晋升id不为0，寻找当前晋升信息
            int nextId = 0;                     //下一晋升等级id
            for (PromotionInfo tmpPromotionInfo : promotionInfoList) {
                if (tmpPromotionInfo.getId() == userPatrons.getPromotionId()) {
                    oldQualification = Long.parseLong(tmpPromotionInfo.getDefaultAttribute());
                    oldSkillList = tmpPromotionInfo.getSkillList();
                    oldTalentList = tmpPromotionInfo.getTalentList();
                    nextId = tmpPromotionInfo.getNextId();
                    break;
                }
            }
            //晋升信息为晋升表通过nextid取得
            if (nextId != 0) {
                for (PromotionInfo promotionInfoInList : promotionInfoList) {
                    if (promotionInfoInList.getId() == nextId) {
                        promotionInfo = promotionInfoInList;
                        break;
                    }
                }
            } else {
                //next为0 满级了不能再升
                return GameErrorCode.E_PATRONS_PROMOTE_LEVEL_MAX;
            }
        }
        if (null == promotionInfo.getParamList() || oldSkillList.size() == 0) {
            return GameErrorCode.E_PATRONS_CAN_NOT_PROMOTE;
        }
        //PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsId);
        //if(patronsInfo != null) {
        //    oldSkillList.addAll(PatronsMgr.calUnlockSkill(userPatrons, patronsInfo));
        //}
        int type = promotionInfo.getType();
        //判断是否有足够条件晋升
        if (type == 1) {//1为门客皮肤拥有几款
            int skinNeed = Integer.parseInt(promotionInfo.getParamList());
            if (patronSkinIdList.size() < skinNeed) {
                return GameErrorCode.E_PATRONS_PROMOTE_SKIN_NOENOUGH;
            }
        } else if (type == 2) {//2为拥有列表中全部服装
            List<Integer> skinNeeds = StringUtils.stringToIntegerList(promotionInfo.getParamList(), ";");
            if (skinNeeds.isEmpty() || !allSkinIdList.containsAll(skinNeeds)) {
                return GameErrorCode.E_PATRONS_PROMOTE_SKIN_NOENOUGH;
            }
        } else if (type == 3) {//3为拥有列表中一定数量的服装
            List<String> paramList = StringUtils.stringToStringList(promotionInfo.getParamList(), "-");
            if (paramList.isEmpty() || paramList.size() < 2) {
                return GameErrorCode.E_PATRONS_CAN_NOT_PROMOTE;
            }
            int count = 0;
            int needNum = Integer.parseInt(paramList.get(0));
            List<Integer> skinNeeds = StringUtils.stringToIntegerList(paramList.get(1), "\\|");
            for (int skinNeed : skinNeeds) {
                for (int hasSkin : allSkinIdList) {
                    if (hasSkin == skinNeed) {
                        count++;
                        break;
                    }
                }
            }
            if (count < needNum) {
                return GameErrorCode.E_PATRONS_PROMOTE_SKIN_NOENOUGH;
            }
        } else if (type == 4) {//3为拥有列表中一定数量的服装（每一种里面都有可选）
            List<String> paramList = StringUtils.stringToStringList(promotionInfo.getParamList(), "-");
            if (paramList.isEmpty() || paramList.size() < 2) {
                return GameErrorCode.E_PATRONS_CAN_NOT_PROMOTE;
            }
            int count = 0;
            int needNum = Integer.parseInt(paramList.get(0));
            String[] skinParams = paramList.get(1).split("\\|");
            for (String skinArr : skinParams) {
                List<Integer> skinNeeds = StringUtils.stringToIntegerList(skinArr, ";");
                boolean find = false;
                for (int skinId : skinNeeds) {
                    for (int hasSkin : allSkinIdList) {
                        if (hasSkin == skinId) {
                            count++;
                            find = true;
                            break;
                        }
                    }
                    if (find) {
                        break;
                    }
                }
            }
            if (count < needNum) {
                return GameErrorCode.E_PATRONS_PROMOTE_SKIN_NOENOUGH;
            }
        } else if (type == 5) {//依赖其他技能等级
            List<Integer> paramList = StringUtils.stringToIntegerList(promotionInfo.getParamList(), "=");
            int skillId = paramList.get(0);
            int skillLv = paramList.get(1);
            UserPatronsSkill skill = null;
            for (UserPatronsSkill patronsSkill : userPatrons.getSkillList()) {
                if (patronsSkill.getSkillId() == skillId) {
                    skill = patronsSkill;
                    break;
                }
            }
            if (skill == null || skill.getSkillLv() < skillLv) {
                return GameErrorCode.E_PATRONS_CAN_NOT_PROMOTE;
            }
        } else if (type == 6) {//消耗材料晋升
            Property cost = PropertyHelper.parseStringToProperty(promotionInfo.getParamList());
            if (!player.getModule(CurrencyModule.class).removeCurrency(cost, eLogMoneyType.PatronsPromotion, eLogMoneyType.PatronsPromotionCost)) {
                return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
            }

        } else {
            return GameErrorCode.E_PATRONS_PROMOTE_TYPE_ERROR;
        }

        List<Integer> newSkillList = promotionInfo.getSkillList();
        List<Integer> newTalentList = promotionInfo.getTalentList();
        //技能天赋增加删除
        dealWithSkillList(userPatrons, oldSkillList, newSkillList, ePatronsSkillType.Normal);
        dealWithSkillList(userPatrons, oldTalentList, newTalentList, ePatronsSkillType.Talent);
        //其他修改
        userPatrons.setPromotionId(promotionInfo.getId());

        //这里的资质修改最终会在onPatronsChange里修正,所以不用设置值
//        userPatrons.setQualification(userPatrons.getQualification() + Long.parseLong(promotionInfo.getDefaultAttribute()) - oldQualification);

        if (promotionInfo.getType() == 5 || promotionInfo.getType() == 6) {//有信物的晋升类型
            checkKeepsakeOpen(userPatrons);
        }

        if (hasTalent(patronsInfo.getTalentList(), eSkillType.CopyMaxPatronsLevelSkill.getValue())) {
            refreshCopyMaxPatronsLevel(false);
        }

        PatronsProto.PatronsPromoteRespMsg.Builder respMsg = PatronsProto.PatronsPromoteRespMsg.newBuilder();
        respMsg.setRet(0);

        Property reward = PropertyHelper.parseStringToProperty(promotionInfo.getReward());
        if (!reward.isNothing()) {//晋升额外奖励
            player.getModule(CurrencyModule.class).addCurrency(reward, eLogMoneyType.Patrons, eLogMoneyType.PatronsPromoteReward);
            respMsg.setReward(promotionInfo.getReward());
        }

        initSkill(userPatrons, patronsInfo);
        onPatronsChange(null);
        syncPatronsData(new ArrayList<>(userPatronsMap.values()), true);

        //晋升同步议事厅
        CrossUnionMgr.userPatronDispatchSkillChangeNotify(player.getUserInfo().getUnionUid(), player.getUserId(), userPatrons);

        //无双基金任务通知
        player.notifyListener(eGamePlayerEventType.NewPatronsFundPatronsQua.getValue(), 0);//新门客基金最新版-门客资质
        player.notifyListener(eGamePlayerEventType.NewPatronsFundBeautyCh.getValue(), 0);//新门客基金最新版-美女才华
        player.notifyListener(eGamePlayerEventType.NewPatronsFundPatronsSkill.getValue(), 0);//新门客基金最新版-门客技能

        PatronsProto.PatronsTempMsg.Builder patronsMsg = PatronsPb.parsePatronsTempMsg(userPatrons);
        if (null != patronsMsg) {
            respMsg.setPatrons(patronsMsg);
        }
        player.sendPacket(Protocol.U_PATRONS_PROMOTE, respMsg);
        return 0;
    }

    /**
     * 检查信物系统解锁
     */
    private void checkKeepsakeOpen(UserPatrons userPatrons) {
        int quality;
        int promotionType = 0;
        HashSet<Integer> promotionSkillSet = new HashSet<>();
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
        if (patronsInfo == null) {
            return;
        }
        quality = patronsInfo.getQuality();
        if (userPatrons.getPromotionId() > 0) {
            PromotionInfo promotionInfo = PromotionMgr.getPromotionInfoById(userPatrons.getPromotionId());
            if (promotionInfo != null) {
                quality = promotionInfo.getQuality();
                promotionType = promotionInfo.getType();
                promotionSkillSet = new HashSet<>(promotionInfo.getSkillList());
            }
        }

        if (promotionType == 5) {
            if (promotionSkillSet.contains(patronsInfo.getKeepsake()) || (quality >= 4 && patronsInfo.getKeepsake() > 0)) { //晋升类型5,梅长苏用,固定品质5解锁信物,
                // 需求改了要品质4就能解锁,然后晋升表里又没配,所以这里改成大于等于4
                UserPatronsSkill patronsSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), patronsInfo.getKeepsake());
                if (patronsSkill == null) {
                    //特殊判断，防止技能重复。因为初始技能类型问题，信物技能除梅长苏外，都改成走晋升表的技能
                    patronsSkill = userPatrons.getPatronsSkill(eSkillType.PatronsKeepSakeAddition.getValue(), patronsInfo.getKeepsake());
                    if (patronsSkill == null) {
                        patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, patronsInfo.getKeepsake(), ePatronsSkillType.Normal.getValue(), 1, 0);
                        addPatronsSkill(userPatrons, patronsSkill);
                    }
                }
            }
        } else if (promotionType == 6) {//晋升类型6,兰飞鸿,晋升有包含信物技能时,解锁信物技能,更新晋升技能
            if (promotionSkillSet.contains(patronsInfo.getKeepsake())) {
                UserPatronsSkill patronsSkill = userPatrons.getPatronsSkill(ePatronsSkillType.Normal.getValue(), patronsInfo.getKeepsake());
                if (patronsSkill == null) {
                    patronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, patronsInfo.getKeepsake(), ePatronsSkillType.Normal.getValue(), 1, 0);
                    addPatronsSkill(userPatrons, patronsSkill);
                }
            }
        }

    }

    /**
     * 信物-门客羁绊
     */
    public int keepSakeRelation(int patronsId, int selectPatronsId) {
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (userPatrons == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
        if (patronsInfo == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }
        if (patronsInfo.getKeepsake() <= 0) {
            return GameErrorCode.E_PATRONS_KEEP_SAKE_SKILL_NO_INIT;
        }
        UserPatronsSkill patronsSkill = userPatrons.getPatronsSkill(patronsInfo.getKeepsake());
        if (patronsSkill == null) {
            return GameErrorCode.E_PATRONS_KEEP_SAKE_SKILL_NO_INIT;
        }
        int oldSelectPatronsId = userPatrons.getKeepSakePatronsId();
        if(oldSelectPatronsId == selectPatronsId){
            return GameErrorCode.E_REQUEST_ARGUMENT_ERROR;
        }
        if (selectPatronsId > 0) {
            UserPatrons selectPatrons = getUserPatrons(selectPatronsId);
            if (selectPatrons == null) {
                return GameErrorCode.E_PATRONS_NO_FOUND;
            }
        }

        //cd
        int now = DateHelper.getCurrentSecond();
        if (userPatrons.getKeepSakeTime() > 0 && now - userPatrons.getKeepSakeTime() <= GameConfig.KEEPSAKE_CD * 60) {
            return GameErrorCode.E_PATRONS_KEEP_SAKE_RELATION_CD;
        }

        if(selectPatronsId > 0) {
            int ret = checkSelectPatronsBeSelectNum(selectPatronsId);
            if (ret > 0) {
                return ret;
            }
        }
        if (selectPatronsId > 0) {
            userPatrons.setKeepSakeTime(now);
        }

        if (selectPatronsId <= 0) {
            userPatrons.setKeepSakeAddition(0);
            userPatrons.setKeepSakePatronsId(0);
        } else {
            userPatrons.setKeepSakePatronsId(selectPatronsId);
            //这边不计算, 门客能力计算的时候一起
        }

        List<UserPatrons> changeList = new ArrayList<>();

        changeList.add(userPatrons);
        if (selectPatronsId > 0) {
            changeList.add(getUserPatrons(selectPatronsId));
        }

        if (oldSelectPatronsId > 0) {
            UserPatrons oldPatrons = getUserPatrons(oldSelectPatronsId);
            if (oldPatrons != null) {
                changeList.add(oldPatrons);
            }
        }

        onPatronsListChange(changeList);

        PatronsProto.PatronsKeepSakeRelationAddRespMsg.Builder respMsg = PatronsProto.PatronsKeepSakeRelationAddRespMsg.newBuilder();
        respMsg.setRet(0);
        for (UserPatrons patrons : changeList) {
            PatronsProto.PatronsTempMsg.Builder patronsMsg = PatronsPb.parsePatronsTempMsg(patrons);
            if (null != patronsMsg) {
                respMsg.addPatrons(patronsMsg);
            }
        }
        player.sendPacket(Protocol.U_PATRONS_KEEP_SAKE_RELATION_ADD, respMsg);

        return 0;
    }

    /**
     * 找一下选择的门客被其他门客绑定的次数
     *
     * @param selectPatronsId
     * @return
     */
    public int checkSelectPatronsBeSelectNum(int selectPatronsId) {
        int hadBind = 0;
        for (UserPatrons patrons : userPatronsMap.values()) {
            if (patrons.getShowKeepSakePatronsId() == selectPatronsId) {
                hadBind++;
            }
        }
        int selectPatronsSakeSkillLv = 0;
        UserPatrons selectPatrons = getUserPatrons(selectPatronsId);
        //选择的门客有没有信物解锁
        UserPatronsSkill selectPatronsSakeSkill = null;
        for (UserPatronsSkill skill : selectPatrons.getSkillList()) {
            SkillInfo skillInfo = SkillMgr.getSkillInfo(skill.getSkillId());
            if (skillInfo.getType() == eSkillType.PatronsKeepSakeLinkAddtition.getValue()) {
                selectPatronsSakeSkill = skill;
                break;
            }
        }
        if (selectPatronsSakeSkill != null) {
            selectPatronsSakeSkillLv = selectPatronsSakeSkill.getSkillLv();
        }
        int canBind = ConfigMgr.getPatronSakeBindNum(selectPatronsSakeSkillLv);
        if (hadBind >= canBind) {
            return GameErrorCode.E_PATRONS_KEEP_SAKE_CAN_NOT_REPEATED;
        }
        return 0;
    }

    //传入新旧技能表进行比对替换删除  数组同位置由有数字变更为0为删除
    private void dealWithSkillList(UserPatrons userPatrons, List<Integer> oldSkillList, List<Integer> newSkillList, ePatronsSkillType type) {
        if (newSkillList.size() < oldSkillList.size()) {
            return;
        }
        //严格按位置比较，是不是不灵活了？？
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
        for (int i = 0; i < oldSkillList.size(); i++) {
            //删除技能
            if (oldSkillList.get(i) != 0 && newSkillList.get(i) == 0) {
                deletePatronsSkill(userPatrons, oldSkillList.get(i));
            }
            //天赋技能替换,目前替换只考虑天赋，添加技能后更新等级
            else if (!oldSkillList.get(i).equals(newSkillList.get(i)) && oldSkillList.get(i) != 0 && newSkillList.get(i) != 0) {
                //需要解锁的，不加
                if (patronsInfo.getSkillUnLockMap().containsKey(newSkillList.get(i))) {
                    continue;
                }
                //还存在的不删
                if (!newSkillList.contains(oldSkillList.get(i))) {
                    deletePatronsSkill(userPatrons, oldSkillList.get(i));
                }
                //已存在的不加
                if (userPatrons.isContainsSkill(type.getValue(), newSkillList.get(i))) {
                    continue;
                }
                UserPatronsSkill userPatronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, newSkillList.get(i), type.getValue(), 1, 0);
                addPatronsSkill(userPatrons, userPatronsSkill);
                calcTalentSkillAddition(userPatrons);
            }
            //技能恢复，暂时没有这种配置，不会运行到这
            else if (oldSkillList.get(i) == 0 && newSkillList.get(i) != 0) {
                addPatronsSkill(userPatrons, PatronsMgr.initUserPatronsSkill(userPatrons, newSkillList.get(i), type.getValue(), 1, 0));
            }
        }
        //天赋技能新增
        if (newSkillList.size() > oldSkillList.size()) {
            for (int i = oldSkillList.size(); i < newSkillList.size(); i++) {
                if (newSkillList.get(i) == 0) {
                    continue;
                }
                //需要解锁的，不加
                if (patronsInfo.getSkillUnLockMap().containsKey(newSkillList.get(i))) {
                    continue;
                }
                //已存在的不加
                if (userPatrons.isContainsSkill(type.getValue(), newSkillList.get(i))) {
                    continue;
                }
                UserPatronsSkill userPatronsSkill = PatronsMgr.initUserPatronsSkill(userPatrons, newSkillList.get(i), type.getValue(), 1, 0);
                addPatronsSkill(userPatrons, userPatronsSkill);
            }
        }
        //新增天赋后计算精进等级
        if (type == ePatronsSkillType.Talent) {
            calcTalentSkillAddition(userPatrons);
        }
    }

    /**
     * 添加技能方法
     */
    public void addPatronsSkill(UserPatrons userPatrons, UserPatronsSkill userPatronsSkill) {
        //如果技能表内已有这个技能
        for (UserPatronsSkill hasUserPatronsSkill : userPatrons.getOriginalSkillList()) {
            if (hasUserPatronsSkill.getSkillId() == userPatronsSkill.getSkillId()) {
                if (hasUserPatronsSkill.getIsDelete() == 1) {
                    hasUserPatronsSkill.setIsDelete(0);
                    hasUserPatronsSkill.setSkillLv(1);
                    hasUserPatronsSkill.setUpdateOption();
                    return;
                } else return;
            }
        }
        //加入新技能
        userPatrons.getOriginalSkillList().add(userPatronsSkill);
    }

    /**
     * 假删除技能 0为未删除 1为删除
     */
    private void deletePatronsSkill(UserPatrons userPatrons, int skillId) {
        for (UserPatronsSkill userPatronsSkill : userPatrons.getSkillList()) {
            if (userPatronsSkill.getSkillId() == skillId) {
                userPatronsSkill.setIsDelete(1);
                userPatronsSkill.setUpdateOption();
                break;
            }
        }
    }

    public int getSkillAddition(int type, int param) {
        return SkillMgr.getSkillAdditionFromMap(type, param, talentSkillAdditionMap);
    }

    /**
     * 获取赚钱最高的门客
     * @return
     */
    public UserPatrons getMaxEarnPatrons() {
        UserPatrons maxEarnUserBeauty = null;
        if (userPatronsMap != null) {
            for (UserPatrons item : userPatronsMap.values()) {
                if (maxEarnUserBeauty == null) {
                    maxEarnUserBeauty = item;
                    continue;
                }
                if (maxEarnUserBeauty.getAbility().compareTo(item.getAbility()) < 0) {
                    // 比较赚钱
                    maxEarnUserBeauty = item;
                } else if (maxEarnUserBeauty.getAbility().compareTo(item.getAbility()) == 0) {
                    if (maxEarnUserBeauty.getPatronsId() < item.getPatronsId()) {
                        maxEarnUserBeauty = item;
                    }
                }
            }
        }
        return maxEarnUserBeauty;
    }

    /**
     * 获取该职业赚钱最高的门客
     * @return
     */
    public UserPatrons getMaxEarnPatronsByType(int type) {
        UserPatrons maxEarnUserPatron = null;
        if (userPatronsMap != null) {
            for (UserPatrons item : userPatronsMap.values()) {

                PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(item.getPatronsId());
                if (patronsInfo == null) {
                    continue;
                }

                if (patronsInfo.getOccupation() != type) {
                    continue;
                }
                if (maxEarnUserPatron == null) {
                    maxEarnUserPatron = item;
                    continue;
                }
                if (maxEarnUserPatron.getAbility().compareTo(item.getAbility()) < 0) {
                    // 比较赚钱
                    maxEarnUserPatron = item;
                } else if (maxEarnUserPatron.getAbility().compareTo(item.getAbility()) == 0) {
                    if (maxEarnUserPatron.getPatronsId() < item.getPatronsId()) {
                        maxEarnUserPatron = item;
                    }
                }
            }
        }
        return maxEarnUserPatron;
    }


    /**
     * 添加授学经验
     */
    public void addTeachExp(int patronsId, long value) {
        UserPatrons patrons = getUserPatrons(patronsId);
        if (patrons != null) {
            patrons.setTeacherExp(patrons.getTeacherExp() + value);
            player.getModule(PatronsModule.class).syncPatronsData(patrons);
        } else {
            log.error("add patronsId ={} teach exp = {} fail!", patrons, value);
        }
    }

    public void checkPatronsTeacherSkill(int patronsId) {
        List<UserPatrons> list = new ArrayList<>();
        if (patronsId == 0) {
            list.addAll(userPatronsMap.values());
        } else {
            UserPatrons userPatrons = getUserPatrons(patronsId);
            if (userPatrons != null) {
                list.add(userPatrons);
            }
        }
        for (UserPatrons userPatrons : list) {
            initTeacherSkill(userPatrons);
        }
        syncPatronsData(list, false);
    }

    public void addGrandChildSpeed(int patronsId, BigInteger earnSpeed) {
        UserPatrons patrons = getUserPatrons(patronsId);
        if (patrons != null) {
            patrons.setGrandChildSpeed(patrons.getGrandChildSpeed().add(earnSpeed));
//            onPatronsChange(patrons);
//            syncPatronsData(patrons);
            // 加到同步缓存列表
            syncPatronsCache(Collections.singletonList(patrons));
        }
    }

    /**
     * 消耗经验升级的技能是否均已满级
     */
    public boolean checkSkillMaxLevel(int patronsId, Integer... skillConsumeIds) {
        List<Integer> skillConsumeIdList = new ArrayList<>(Arrays.asList(skillConsumeIds));
        UserPatrons userPatrons = getUserPatrons(patronsId);
        boolean isMaxLevel = true;
        for (UserPatronsSkill skill : userPatrons.getSkillList()) {
            int skillLv = skill.getSkillLv();
            SkillInfo skillInfo = SkillMgr.getSkillInfo(skill.getSkillId());
            for (int i = 0; i < skillInfo.getAllUpgradeType().size(); i++) {
                int consumeId = skillInfo.getConsumeGoodsId().get(i);
                if (skillConsumeIdList.contains(consumeId)) {
                    int maxLevel = getMaxLevel(skill, skillInfo, skill.getType(), userPatrons.getPatronsId());
                    if (skillLv < maxLevel) {
                        isMaxLevel = false;
                        break;
                    }
                }
            }
        }
        return isMaxLevel;
    }

    /**
     * 获取门客LIST,根据职业and skillConsumeId
     * @param occupation
     * @param skillConsumeId
     * @return
     */
    public List<UserPatrons> getPatronsListBySkillConsumeId(int occupation, int skillConsumeId) {
        List<UserPatrons> dataList = new ArrayList<>();
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            boolean isAdd = false;
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            if (patronsInfo.getOccupation() == occupation || 0 == occupation) {
                for (UserPatronsSkill skill : userPatrons.getSkillList()) {
                    int skillLv = skill.getSkillLv();
                    SkillInfo skillInfo = SkillMgr.getSkillInfo(skill.getSkillId());
                    for (int i = 0; i < skillInfo.getAllUpgradeType().size(); i++) {
                        int consumeId = skillInfo.getConsumeGoodsId().get(i);
                        if (consumeId == skillConsumeId) {
                            int maxLevel = getMaxLevel(skill, skillInfo, skill.getType(), patronsInfo.getId());
                            if (skillLv < maxLevel) {
                                dataList.add(userPatrons);
                                isAdd = true;
                                break;
                            }
                        }
                    }
                    if (isAdd) {
                        break;
                    }
                }
            }
        }
        return dataList;
    }

    /**
     * 随机获取门客
     * @param occupation
     * @param skillConsumeId
     * @return
     */
    public UserPatrons getRandomPatronsBySkillConsumeId(int occupation, int skillConsumeId) {
        List<UserPatrons> dataList = this.getPatronsListBySkillConsumeId(occupation, skillConsumeId);
        return this.getRandomPatronsByList(dataList);
    }

    /**
     *  随机获取门客, 根据门客LIST
     * @param userPatronsList
     * @return
     */
    public UserPatrons getRandomPatronsByList(List<UserPatrons> userPatronsList) {
        if (userPatronsList != null && userPatronsList.size() > 0) {
            int randomValue = threadSafeRandom.next(0, userPatronsList.size());
            return userPatronsList.get(randomValue);
        }
        return null;
    }

    /**
     * 按直接获取门客
     */
    public List<UserPatrons> getUserPatronsList(int occupation) {
        List<UserPatrons> list = new ArrayList<>();
        for (UserPatrons userPatrons : getUserPatronsList()) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            if (patronsInfo != null) {
                if (patronsInfo.getOccupation() == occupation) {
                    list.add(userPatrons);
                }
            }
        }
        return list;
    }

    /**
     * 获取加资质的门客
     *
     * @param specialBelongRole 皮肤info里特殊的belongRole
     * @return null表示不支持这个参数
     */
    public List<UserPatrons> getAddQuaPatronsList(int specialBelongRole) {
        int occupation = SkinMgr.changeToPatronsOccupation(specialBelongRole);
        if (occupation == -1) {
            return null;
        } else if (occupation == 0) {
            return getUserPatronsList();
        } else {
            return getUserPatronsList(occupation);
        }
    }

    /**
     * 获取指定技能列表等级上限加成
     * @return
     */
    public int getSkillMaxLevelAddValue() {
        int addLv = 0;
        //厢房加成
        addLv += WingRoomMgr.getSkillMaxLevelAdd(player.getModule(WingRoomModule.class).getWingRoomData());
        //酒坊加成
        int skillMaxLevelAdd = this.player.getModule(WineryModule.class).getSkillMaxLevelAdd();
        addLv += skillMaxLevelAdd;
        //妙音坊加成
        addLv += player.getModule(MusicRoomModule.class).getSkillMaxLevelAdd();
        //藏宝系统加成
        addLv += player.getModule(CurioModule.class).getSkillMaxLevelAdd();
        return addLv;
    }

    public List<Long> getTongBingSoldierAddition() {
        List<Long> list = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            int skillAddition = getTalentSkillAddition(eSkillType.TongBingTeamMaxSoldierAddi.getValue(), i);
            list.add((long) skillAddition);
        }
        long addition = CurioMgr.getSkillAdditionBySkillType(eSkillType.TongBingSoldierSkill.getValue(), player, 0);
        list.add(addition);
        return list;
    }

    public List<Long> getChuHanSoldierAddition() {
        List<Long> list = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            int skillAddition = getTalentSkillAddition(eSkillType.ChuHanTeamMaxSoldierAddi.getValue(), i);
            list.add((long) skillAddition);
        }
//        long addition = CurioMgr.getSkillAdditionBySkillType(eSkillType.TongBingSoldierSkill.getValue(), player, 0);
//        list.add(addition);
        return list;
    }

    public List<Long> getTonBingSoldierAddition() {
        List<Long> list = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            int skillAddition = getTalentSkillAddition(eSkillType.TongBingTeamMaxSoldierAddi.getValue(), i);
            list.add((long) skillAddition);
        }
        long addition = CurioMgr.getSkillAdditionBySkillType(eSkillType.TongBingSoldierSkill.getValue(), player, 0);
        list.add(addition);
        return list;
    }

    public long getSuiTangSoldierAddition() {
        long ret = 0;
        int skillAddition = getTalentSkillAddition(eSkillType.SuiTangTeamMaxSoldierAddi.getValue(), 0);
        ret += skillAddition;
        ret += CurioMgr.getSkillAdditionBySkillType(eSkillType.CurioSuiTangTeamMaxSoldierAdd.getValue(), player, 0);
        return ret;
    }

    public long getSuiTangPatronsNSum(int n) {
        long ret = 0;
        for (int i = 1; i <= 5; i++) {
            List<UserPatrons> userPatronsList = getUserPatronsList(i);
            ret += Math.min(n, userPatronsList.size());
        }
        return ret;
    }

    public long getSuiTangPatronsNAbility(int n) {
        long ret = 0;
        for (int i = 1; i <= 5; i++) {
            List<UserPatrons> userPatronsList = getUserPatronsList(i);
            userPatronsList.sort((o1, o2) -> {
                BigInteger a1 = o1.getAbility();
                BigInteger a2 = o2.getAbility();
                return a2.compareTo(a1);
            });
            for (int j = 0; j < Math.min(n, userPatronsList.size()); j++) {
                ret += userPatronsList.get(j).getAbility().longValue();
            }
        }
        return ret;
    }

    /**
     * 获取前几门客
     * @param topCount
     * @return
     */
    public List<UserPatrons> getTopAbilityPatrons(int topCount) {
        List<UserPatrons> userPatrons = getUserPatronsList();
        List<UserPatrons> resultList = new ArrayList<>();
        userPatrons.sort((o1, o2) -> {
            BigInteger a1 = o1.getAbility();
            BigInteger a2 = o2.getAbility();
            return a2.compareTo(a1);
        });

        for (UserPatrons patrons : userPatrons) {
            resultList.add(patrons);
            if (--topCount <= 0) {
                break;
            }
        }

        return resultList;
    }

    /**
     * 获取前几门客id
     * @param topCount
     * @return
     */
    public List<Integer> getTopAbilityPatronsIdList(int topCount) {
        List<UserPatrons> list = this.getTopAbilityPatrons(topCount);
        List<Integer> result = new ArrayList<>();
        for (UserPatrons patrons : list) {
            result.add(patrons.getPatronsId());
        }
        return result;
    }

    /**
     * 获取前几门客总战力
     * @param topCount
     * @return
     */
    public long getTopAbilityPatronsTotalAbility(int topCount) {
        List<UserPatrons> list = this.getTopAbilityPatrons(topCount);
        long totalAbility = 0;
        for (UserPatrons patrons : list) {
            totalAbility += patrons.getAbility().longValue();
        }
        return totalAbility;
    }

    /**
     * 获取前N门客赚钱均值
     * @param topCount
     * @return
     */
    public long getTopAbilityPatronsAveAbility(int topCount) {
        long totalAbility = this.getTopAbilityPatronsTotalAbility(topCount);
        return totalAbility / topCount;
    }

    /**
     * 获取门客资质。排除【珍兽】【鱼类】【蛐蛐】【信物】带来的资质
     * @param patronsId 门客ID
     * @return long
     */
    public long getQualificationExcludeEq(int patronsId) {
        UserPatrons patrons = this.userPatronsMap.get(patronsId);
        if (patrons == null) {
            return 0;
        }
        //排除的资质
        long excludeValue = 0;
        //获取坐骑纸质
        UserVehicle userVehicle = player.getModule(VehicleModule.class).getUserVehicleByPatrons(patronsId);
        if (userVehicle != null) {
            excludeValue += VehicleMgr.getVehicleQualification(userVehicle.getTemplateId(), userVehicle.getLevel());
        }
        //获取鱼类资质
        Map<Integer, FishData> fishDataMap = player.getModule(ManorModule.class).getFishDataMap();
        excludeValue += FishPondMgr.getFishAddQualification(patronsId, fishDataMap);
        //获取蛐蛐资质
        Map<Integer, CricketData> cricketDataMap = player.getModule(CricketModule.class).getCricketDataMap();
        excludeValue += CricketMgr.getCricketAddQualification(patronsId, cricketDataMap);
        //返回结果
        return patrons.getQualification() - excludeValue;
    }

    /**
     * 同步门客数据(缓存)
     */
    public void syncPatronsCacheByPatronsIds(List<Integer> patronsIds) {
        synchronized (needSyncPatrons) {
            for (Integer patronsId : patronsIds) {
                needSyncPatrons.add(getUserPatrons(patronsId));
            }
        }
    }

    /**
     * 同步门客数据(缓存)
     */
    public void syncPatronsCache(List<UserPatrons> patronsList) {
        synchronized (needSyncPatrons) {
            needSyncPatrons.addAll(patronsList);
        }
    }

    public void syncPatronsCache(int patronsId) {
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (userPatrons == null) {
            return;
        }
        synchronized (needSyncPatrons) {
            needSyncPatrons.add(userPatrons);
        }
    }

    /**
     * 提交变动
     */
    public void commitPatronsChange() {
        List<UserPatrons> patronsList;
        synchronized (needSyncPatrons) {
            patronsList = new ArrayList<>(needSyncPatrons);
            needSyncPatrons.clear();
        }
        if (patronsList.isEmpty()) {
            return;
        }
        onPatronsListChange(patronsList);
        synchronized (needSyncPatrons) {
            needSyncPatrons.clear();
        }
        //能力计算后再通知
        for (UserPatrons patrons : patronsList) {
            notifyPatronsDispatch(patrons);
        }
        // 同步
        syncPatronsData(patronsList, false);

        if (updateHistoryMaxEarnSpeed.getAndSet(false)) {
            player.getModule(PlayerModule.class).syncUserHistoryAttribute();
        }
    }

    /**
     * 刷新门客最大等级并复制,有特殊天赋的要过滤掉
     */
    public void refreshCopyMaxPatronsLevel(boolean isLoad) {
        int maxLevel = 0;
        int maxBreakLevel = 0;

        UserPatrons tempMaxLevelUserPatrons = null;

        List<UserPatrons> copyLevelPatronsList = new ArrayList<>();//有复制等级技能的门客

        for (UserPatrons userPatrons : userPatronsMap.values()) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            if (patronsInfo == null) {
                continue;
            }
            if (hasTalent(patronsInfo.getTalentList(), eSkillType.CopyMaxPatronsLevelSkill.getValue())) {
                copyLevelPatronsList.add(userPatrons);
                continue;
            }
            if (userPatrons.getLevel() >= maxLevel) {
                if (userPatrons.getBreakLv() >= maxBreakLevel) {
                    maxLevel = userPatrons.getLevel();
                    maxBreakLevel = userPatrons.getBreakLv();
                    tempMaxLevelUserPatrons = userPatrons;
                }
            }
        }

        this.maxLevelUserPatrons = tempMaxLevelUserPatrons;

        if (this.maxLevelUserPatrons == null) {
            return;
        }

        List<UserPatrons> changeList = new ArrayList<>();

        for (UserPatrons userPatrons : copyLevelPatronsList) {
            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(userPatrons.getPatronsId());
            if (patronsInfo == null) {
                continue;
            }

            int initQuality = patronsInfo.getQuality();
            int promotionTimes = 0;

            int promotionId = userPatrons.getShowPromotionId();
            if (promotionId != 0) {
                PromotionInfo promotionInfo = PromotionMgr.getPromotionInfoById(promotionId);
                if (null != promotionInfo) {
                    int quality = promotionInfo.getQuality();
                    promotionTimes = quality - initQuality;
                }
            }

            for (Integer talentSkillId : patronsInfo.getTalentList()) {
                SkillInfo skillInfo = SkillMgr.getSkillInfo(talentSkillId);
                if (skillInfo == null) {
                    continue;
                }
                if (skillInfo.getType() == eSkillType.CopyMaxPatronsLevelSkill.getValue()) {

                    UserPatronsSkill patronsSkill = userPatrons.getPatronsSkill(skillInfo.getId());
                    if (patronsSkill != null) { //这个技能等级等于晋升次数+1
                        if (patronsSkill.getSkillLv() != promotionTimes + 1) {
                            patronsSkill.setSkillLv(promotionTimes + 1);
                        }
                    }

                    int copyMaxLevel = skillInfo.getParamList().get(promotionTimes).intValue();//复制的最高等级
                    int copyMaxBreakLv = 0;
                    PatronsBreak breakConfig = PatronsMgr.findPatronsLvMaxBreakLv(copyMaxLevel);
                    if (breakConfig != null) {
                        copyMaxBreakLv = breakConfig.getBreakLv();
                    }

                    int copyLevel = Math.min(this.maxLevelUserPatrons.getLevel(), copyMaxLevel);
                    int copyBreakLv = Math.min(this.maxLevelUserPatrons.getBreakLv(), copyMaxBreakLv);
                    int oldLevel = userPatrons.getLevel();
                    if (copyLevel != oldLevel || copyBreakLv != userPatrons.getBreakLv()) {
                        //加个日志
                        if (copyBreakLv != userPatrons.getBreakLv()) {
                            // 门客突破日志
                            AutoLogMgr.add(new LogPatronsBreak(player.getUserId(), userPatrons.getPatronsId(), userPatrons.getBreakLv(), copyBreakLv, ""));
                        }
                        if (copyLevel != oldLevel) {
                            // 门客升级日志
                            AutoLogMgr.add(new LogPatronsUpgrade(player.getUserId(), userPatrons.getPatronsId(), oldLevel, copyLevel, ""));
                        }

                        userPatrons.setLevel(copyLevel);
                        userPatrons.setBreakLv(copyBreakLv);

                        //门客等级基金
                        player.notifyListener(eGamePlayerEventType.PatronsLevelFund.getValue(), new PatronLevelArgs(userPatrons.getPatronsId(), userPatrons.getLevel()));
                        player.notifyListener(eGamePlayerEventType.HasLevelPatronsCount.getValue(), new PatronsArgs(userPatronsMap));
                        player.notifyListener(eGamePlayerEventType.PatronsTotalLevel.getValue(), new PatronsArgs(userPatronsMap));

                        changeList.add(userPatrons);
                    }
                    break;
                }
            }
        }

        if (isLoad) {
            onPatronsListChange(changeList);
        } else {
            syncPatronsCache(changeList);
        }
    }

    /**
     * 契约美女
     *
     * @param patronsId
     * @param pos
     * @param beautyId
     * @return
     */
    public int contractBeauty(int patronsId, int pos, int beautyId) {
        UserPatrons userPatrons = getUserPatrons(patronsId);
        if (userPatrons == null) {
            return GameErrorCode.E_PATRONS_NO_FOUND;
        }

        UserBeauty userBeauty = player.getModule(BeautyModule.class).getUserBeauty(beautyId);
        if (userBeauty == null) {
            return GameErrorCode.E_BEAUTY_NO_FOUND;
        }

        int posNumMax = 0;
        for (UserPatronsSkill userPatronsSkill : userPatrons.getSkillList()) {
            SkillInfo skillInfo = SkillMgr.getSkillInfo(userPatronsSkill.getSkillId());
            if (skillInfo == null) {
                continue;
            }
            if (skillInfo.getType() == eSkillType.FoxSkill205.getValue()) {
                posNumMax = SkillMgr.getSkillAddition(skillInfo, userPatronsSkill.getSkillLv());
                break;
            }
        }
        if (posNumMax == 0) {
            return GameErrorCode.E_PATRONS_CONTRACT_NOT_POS;
        }

        if (pos > posNumMax || pos <= 0) {
            return GameErrorCode.E_PATRONS_CONTRACT_NOT_POS;
        }

        if (BeautyMgr.beautyAndPatronsHasRelation(userBeauty, userPatrons)) {
            return GameErrorCode.E_PATRONS_CONTRACT_NOT_RELATION;
        }

        UserPatronsContract patronsContract = userPatrons.getPatronsContractMap().get(pos);
        if (patronsContract == null) {
            patronsContract = new UserPatronsContract(getUserId(), patronsId, pos);
            userPatrons.getPatronsContractMap().put(pos, patronsContract);
        }

        if (patronsContract.getBeautyId() == beautyId) {
            return GameErrorCode.E_PATRONS_CONTRACT_REPEAT;
        }

        if (patronsContract.getBeautyId() != 0) {//更换契约美女要消耗元宝
            Property cost = PropertyHelper.parseStringToProperty(GameConfig.PATRONS_CONTRACT_REPLACE_CONSUME);
            if (!player.getModule(CurrencyModule.class).removeCurrency(cost, eLogMoneyType.Patrons, eLogMoneyType.PatronsStage)) {
                return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
            }
        }

        patronsContract.setBeautyId(beautyId);
        patronsContract.setContractTime(DateHelper.getCurrentTime());

        onPatronsChange(userPatrons);

        AutoLogMgr.add(new LogPatronsContract(getUserId(), patronsId, beautyId, pos));

        PatronsProto.PatronsContractBeautyRespMsg.Builder respMsg = PatronsProto.PatronsContractBeautyRespMsg.newBuilder();
        respMsg.setRet(0);
        respMsg.setPatrons(PatronsPb.parsePatronsTempMsg(userPatrons));
        player.sendPacket(ClientProtocol.U_PATRONS_CONTRACT_BEAUTY, respMsg);
        return 0;
    }

    /**
     * 获得契约了这个美女的门客
     *
     * @param beautyId
     * @return
     */
    public int getContractBeautyPatronsId(int beautyId) {
        UserPatrons userPatrons = getContractBeautyPatrons(beautyId);
        if (userPatrons == null) {
            return 0;
        }
        return userPatrons.getPatronsId();
    }

    public UserPatrons getContractBeautyPatrons(int beautyId) {
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            for (UserPatronsContract patronsContract : userPatrons.getPatronsContractMap().values()) {
                if (patronsContract.getBeautyId() == beautyId) {
                    return userPatrons;
                }
            }
        }
        return null;
    }

    public List<UserPatrons> getKeepSakeTargetPatronsIdPatronsList(int patronsId) {
        List<UserPatrons> patronsList = new ArrayList<>();
        for (UserPatrons userPatrons : userPatronsMap.values()) {
            if (userPatrons.getShowKeepSakePatronsId() == patronsId) {
                patronsList.add(userPatrons);
            }
        }

        return patronsList;
    }

    /**
     * 增加多少【资质】，能给门客加多少赚钱
     * @param patronsId        门客ID
     * @param qualificationNum 增加的资质
     * @return
     */
    public BigDecimal qualificationAddAbility(int patronsId, int qualificationNum) {
        UserPatrons userPatrons = this.getUserPatrons(patronsId);
        if (userPatrons != null) {
            //构造门客赚钱值计算参数
            CalcPatronsAbilityParam calcPatronsAbilityParam = this.buildCalcPatronsAbilityParam();
            //计算
            return PatronsMgr.qualificationAddAbility(qualificationNum, userPatrons, calcPatronsAbilityParam);
        }
        return BigDecimal.ZERO;
    }

    /**
     * 加多少【赚钱千分比】，能给门客加多少赚钱
     * @param patronsId 门客ID
     * @param rate 赚钱千分比
     * @return
     */
    public BigDecimal rateAddAbility(int patronsId, int rate) {
        UserPatrons userPatrons = this.getUserPatrons(patronsId);
        if (userPatrons != null) {
            //构造门客赚钱值计算参数
            CalcPatronsAbilityParam calcPatronsAbilityParam = this.buildCalcPatronsAbilityParam();
            //计算
            return PatronsMgr.rateAddAbility(rate, userPatrons, calcPatronsAbilityParam);
        }
        return BigDecimal.ZERO;
    }
}
